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
相关产品推荐
相关产品推荐

