You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native安卓端如何捕获损坏JS Bundle引发的崩溃异常?

解决方案:安卓原生捕获RN Bundle加载异常并实现回滚

1. 自定义JS异常处理器

实现JSExceptionHandler接口,精准捕获目标异常并执行回滚逻辑,同时保留RN默认的异常处理流程。

Kotlin 代码示例

import com.facebook.react.bridge.JSExceptionHandler
import com.facebook.react.bridge.ReactContext

class CustomJSExceptionHandler(
    private val defaultHandler: JSExceptionHandler?,
    private val rollbackAction: () -> Unit
) : JSExceptionHandler {

    override fun handleException(exception: Throwable, isFatal: Boolean) {
        // 匹配目标异常:"Could not get BatchedBridge"
        if (exception is com.facebook.jni.CppException && 
            exception.message?.contains("Could not get BatchedBridge") == true) {
            // 触发Bundle回滚
            rollbackAction.invoke()
        }
        
        // 调用默认处理器,保证正常异常流程不受影响
        defaultHandler?.handleException(exception, isFatal)
    }
}

2. 注入自定义处理器到ReactInstanceManager

在ReactNativeHost的ReactInstanceManager构建流程中,替换默认的异常处理器,同时添加C++层异常处理覆盖更多场景。

Kotlin 代码示例(ReactNativeHost方式)

class MyReactNativeHost(application: Application) : ReactNativeHost(application) {
    private val bundleManager = BundleManager.getInstance(application)

    override fun getReactInstanceManager(): ReactInstanceManager {
        return ReactInstanceManager.builder()
            .setApplication(application)
            .setCurrentActivity(currentActivity)
            .setBundleAssetName("index.android.bundle")
            .setJSMainModulePath("index")
            .addPackage(MainReactPackage())
            // 注入自定义JS异常处理器
            .setJSExceptionHandler(
                CustomJSExceptionHandler(
                    ReactInstanceManager.getDefaultJSExceptionHandler(),
                    { bundleManager.rollbackToLastValidBundle() }
                )
            )
            // 处理C++层抛出的CppException
            .setReactCxxErrorHandler { exception ->
                if (exception is com.facebook.jni.CppException && 
                    exception.message?.contains("Could not get BatchedBridge") == true) {
                    bundleManager.rollbackToLastValidBundle()
                }
            }
            .build()
    }

    override fun getUseDeveloperSupport(): Boolean {
        return BuildConfig.DEBUG
    }

    override fun getPackages(): List<ReactPackage> {
        return PackageList(this).packages
    }
}

3. 捕获初始化阶段的直接异常

部分严重的Bundle损坏会在ReactContext初始化时直接抛出,需在初始化代码外层添加try-catch:

Kotlin 代码示例

// 在Activity/Application初始化ReactInstance时
try {
    reactNativeHost.reactInstanceManager.createReactContextInBackground()
} catch (e: com.facebook.jni.CppException) {
    if (e.message?.contains("Could not get BatchedBridge") == true) {
        BundleManager.getInstance(application).rollbackToLastValidBundle()
        // 回滚后重新初始化
        reactNativeHost.reactInstanceManager.createReactContextInBackground()
    }
}

4. 核心回滚逻辑实现

class BundleManager private constructor(private val context: Context) {
    companion object {
        @Volatile
        private var instance: BundleManager? = null
        fun getInstance(context: Context) = instance ?: synchronized(this) {
            instance ?: BundleManager(context.applicationContext).also { instance = it }
        }
    }

    private val bundleDir = context.filesDir.resolve("rn_bundles")
    private val prefs = context.getSharedPreferences("bundle_prefs", Context.MODE_PRIVATE)
    private val lastValidBundlePath = prefs.getString("last_valid_bundle", null) 
        ?: context.filesDir.resolve("default_bundle.android.bundle").absolutePath

    fun rollbackToLastValidBundle() {
        // 删除损坏的当前Bundle
        val currentBundle = bundleDir.resolve("current_bundle.android.bundle")
        if (currentBundle.exists()) currentBundle.delete()
        
        // 恢复上一个有效Bundle为当前Bundle
        val validBundle = File(lastValidBundlePath)
        if (validBundle.exists()) validBundle.copyTo(currentBundle, overwrite = true)
        
        // 更新本地记录
        prefs.edit()
            .putString("current_bundle", currentBundle.absolutePath)
            .apply()
    }

    // 扩展方法:下载新Bundle、验证Bundle完整性、更新有效Bundle记录等
}

关键注意事项

  • 保留默认处理器:自定义处理器必须调用默认的JSExceptionHandler,避免丢失RN原生的异常上报和调试逻辑。
  • 双重异常覆盖:同时处理JS层和C++层的异常,覆盖Bundle加载的全流程场景。
  • 前置Bundle验证:下载完成后先做基础验证(如文件大小、JS语法头检查),减少损坏Bundle被加载的概率。

内容的提问来源于stack exchange,提问作者shubham jha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 04:55:30