解决ReactNative崩溃:Can't find variable: __fbBatchedBridge

这个坑我花了两天时间才爬出来!!

首先关于这个问题,甭管google、百度还是看issue,大都给的解决方案是
+ 检查packager是否启动了?
+ 尝试adb reverse tcp:8081 tcp:8081
+ 打开dev模式,设置ip:8081(localhost:8081)

通过以上途径,如果解决了这个问题,恭喜你!那么这种情况下通常会紧跟一个——但是!
但是!我尝试了网上的所有方法都没办法解决我的问题,而且发现某个犄角旮旯里有个哥们也碰到和我一样的病症,但那哥们没有给出解决方案,只是说如果手动运行react bundle命令将js打包成bundle并放到项目的asserts目录下就可以正常运行。react bunlde命令其实是react.gradle的核心,我也尝试手工执行该命令打包bundle,发现确实可以正常运行。
那么问题来了!这个命令是在哪个task执行的?查看gradle面板,是在bundleDebugJsAndAssets/bundleReleaseJsAndAssets中执行,好,那么下面编译下看这个命令的执行结果。什么?竟然是bundleReleaseJsAndAssets SKIPPED。
在初始化的react-native的build.gradle的注释中有句这样的话:

“By default,bundleDebugJsAndAssets is skipped,as in debug/dev mode we prefer to load the bundle directly from the development server”

意思是在debug的编译模式下bundleDebugJsAndAssets会默认被skipped,但为什么我执行项目的assembleDebug执行的却是bundleReleaseJsAndAssets而不是bundleDebugJsAndAssets,而且竟然被skipped了?
当时我有两个完全靠第六感的猜测。(有时候第六感对于码农来说也蛮重要的)
第一个猜测是,58项目的application module名字为‘58WuxianClient’,会不会和数字开头有关?不过,我把module名称改为’WubaWuxianClient’后还是不行。第一个猜测失败。
第二个猜测,通过react-native init编译的初始化项目,RN Activity都是在application module中,而在我上一篇文章《如何将RN整合到58NA的Android项目中》描述,我创建了一个名为WubaRNLib的library module,现在启动的RN Activity都在这个module中,会不会和application/module有关系?然后我把WubaRNTestActivity(继承自ReactActivity)从library移动到application的module中。启动,竟然加载成功了!!

OK!那么接下来的矛盾重点就集中在library/application上了。

同样的代码在library和application中竟然两种执行结果,要解决这个问题只能深入到源码中查找答案。不过,这里我还不想讲RN解剖查看实现原理,只想了解ReactActivity的执行流程。好在RN提供了另一种页面实现方式,可以直接继承Activity,只需要实现DefaultHardwareBackBtnHandler接口即可。查看ReactActivity的代码,发现其实它就是这个套路。那么走起~
在WubaRNLib中新建Activity,核心代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mRootView = new ReactRootView(this);
    mManager = ReactInstanceManager.builder()
            .setApplication(getApplication())
            .setBundleAssetName("index.android.bundle")
            .setJSMainModuleName("index.android")
            .addPackage(new MainReactPackage())
            .setUseDeveloperSupport(BuildConfig.DEBUG)
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .build();
    mRootView.startReactApplication(mManager,"WubaRNProV2",null);
    setContentView(mRootView);
}

不出所料,启动这个Activity依然报java.lang.RuntimeException: ReferenceError: Can’t find variable: __fbBatchedBridge并crash,然后挪到application module下,启动,通过。这起码说明代码是没问题的。
在自己创建ReactInstanceManager时需要调用setBundleAssetName和setJSMainModuleName两个方法,这引起了我的注意。还记得上面说到,手动执行react bundle命令将js文件打包成bundle并放到assets下就可以正常运行么?
第六感,没错,还是第六感告诉我,library/application的矛盾解决点很可能和ReactInstanceManager有关系。那么,撸起袖子开始做实验吧。
我分别将这个Activity放到library/application中执行并debug,记录下ReactInstanceManager中的变量来做对比。发现setBundleAssetName和setJSMainModuleName方法设置的值一样,我又猜错了。但是!!(粗体)里面竟然有个mDevSupportManager不太一样。然后深入到源码中发现这个变量和setUseDeveloperSupport()有关。这个方法我传递的值是BuildConfig.DEBUG,因为编译时执行的是gradle assembleDebug,所以可以肯定的是application module编译时BuildConfig.DEBUG为true,但library module呢?
查看library中BuildConfig代码

public static final boolean DEBUG = Boolean.parseBoolean("true");

看上去也是true,没问题啊。算了!不管这么多了,直接把library module中的Activity中调用setUseDeveloperSupport的值改为true试试。结果,竟然执行通过了!看来找到了元凶。也就是说,虽然执行的是assembleDebug,但library module中BuildConfig.DEBUG其实是false。google后发现library在编译aar过程中其实执行的是release模式。这也印证了上面说的为什么执行的是bundleReleaseJsAndAssets而不是bundleDebugJsAndAssets。那么理论上修改gradle将libray module编译模式改成debug应该能解决这个问题。说干就干。
WubaRNLib.gradle

android {
    publishNonDefault true
}

依赖WubaRNLib的module

dependencies {
    releaseCompile project(path: ':WubaRNLib',configuration: 'release')
    debugCompile project(path: ':WubaRNLib',configuration: 'debug')
}

编译,运行,通过! 总结下,解决这个问题主要是用最传统也往往最能解决问题的二分大法,再加上一些直觉。

相关文章

react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接...
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc ...