}
可以看见,ReactRootView 果然是个牛逼的类,我也不多解释了,大段的英文注释已经交代很清楚用途和地位了,我们直接看上面代码的 startReactApplication 方法吧,可以看见他又调用了一个三个参数的同名方法,具体这三个参数来历如下(也是我们自己集成 RN 时手动 builder 模式创建的):
1. reactInstanceManager: 大内总管接口类,提供一个构造者模式的初始化 Builder,实现类是 XReactInstanceManagerImpl,这类也是我们在集成 RN 时 new ReactRootView 的之前自己创建的。
2. moduleName: 与 JS 代码约定的 String 类型识别 name,JS 端通过 AppRegistry.registerComponent 方法设置这个 name,Java 端重写基类的 getMainComponentName 方法设置这个 name,这样两边入口就对上了。
3. launchOptions: 这里默认是 null 的,如果自己不继承 ReactActivity 而自己实现的话可以通过这个参数在 startActivity 时传入一些参数到 JS 代码,用来依据参数初始化 JS 端代码。
这些参数都初始化传递好了以后,可以看见接着调用了 mReactInstanceManager 的 createReactContextInBackground 方法,mReactInstanceManager 就是上面说的第一个参数,实质是通过一个构造者模式创建的,实现类是 XReactInstanceManagerImpl,所以我们直接跳到 XReactInstanceManagerImpl 的 createReactContextInBackground 方法看看,如下:
public void createReactContextInBackground() {
…
recreateReactContextInBackgroundInner();
}
private void recreateReactContextInBackgroundInner() {
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport && mJSMainModuleName != null) {
//如果是 dev 模式,BuildConfig.DEBUG=true就走这里,在线更新bundle,手机晃动出现调试菜单等等。
//这个路线属于RN调试流程原理,后面再写文章分析,这里我们抓住主线分析
…
return;
}
//非调试模式,即BuildConfig.DEBUG=false时执行
recreateReactContextInBackgroundFromBundleLoader();
}
private void recreateReactContextInBackgroundFromBundleLoader() {
//厉害了,word哥,在后台创建ReactContext,两个参数是重点。
//mJSCConfig.getConfigMap()默认是一个WritableNativeMap,在前面通过构造模式构造时通过Builder类的set方法设置。
//mJSBundleLoader是在前面通过构造模式构造时通过Builder类的多个setXXX方法均可设置的,
//最终在Builder中build方法进行判断处理,你可以自定义Loader,或者按照build方法规则即可,
//默认是JSBundleLoader.createAssetLoader静态方法返回的JSBundleLoader抽象类的实现类。
//自定义热更新时setJSBundleFile方法参数就是巧妙的利用这里是走JSBundleLoader.createAssetLoader还是JSBundleLoader.createFileLoader!!!!!!
recreateReactContextInBackground(
new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
mBundleLoader);
}
private void recreateReactContextInBackground(
JavaScriptExecutor.Factory jsExecutorFactory,
JSBundleLoader jsBundleLoader) {
UiThreadUtil.assertOnUiThread();
//封装一把,把两个参数封装成ReactContextInitParams对象
ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
if (mReactContextInitAsyncTask == null) {
//初始化进来一定会走啦,这货不就是创建一个AsyncTask,然后执行,同时传递封装的参数initParams给task。
// No background task to create react context is currently running, create and execute one.
mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
} else {
// Background task is currently running, queue up most recent init params to recreate context
// once task completes.
mPendingReactContextInitParams = initParams;
}
}
通过上面注释可以看见,我们《React Native Android 从学车到补胎和成功发车经历 #3-5 RN 集成后热更新核心思路》的热更新面纱也是从这个地方开始揭晓的,ReactInstanceManager 的 setJSBundleFile 如下:
/**
-
Path to the JS bundle file to be loaded from the file system.
-
Example: {@code “assets://index.android.js” or “/sdcard/main.jsbundle”}
*/
public Builder setJSBundleFile(String jsBundleFile) {
if (jsBundleFile.startsWith(“assets://”)) {
mJSBundleAssetUrl = jsBundleFile;
mJSBundleLoader = null;
return this;
}
return setJSBundleLoader(JSBundleLoader.createFileLoader(jsBundleFile));
}
我们先记住这个提示,后面再边分析主加载流程边插入介绍热更新的原理,所以我们还是把思路先回到 XReactInstanceManagerImpl 内部类的 ReactContextInitAsyncTask 上,如下:
private final class ReactContextInitAsyncTask extends
AsyncTask<ReactContextInitParams, Void, Result> {
…
@Override
protected Result doInBackground(ReactContextInitParams… params) {
…
try {
//异步执行的重量级核心方法createReactContext,创建ReactContext
JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
} catch (Exception e) {
// Pass exception to onPostExecute() so it can be handled on the main thread
return Result.of(e);
}
}
@Override
protected void onPostExecute(Result result) {
try {
//回到主线程执行的重量级核心方法setupReactContext,设置ReactContext相关
setupReactContext(result.get());
} catch (Exception e) {
mDevSupportManager.handleException(e);
} finally {
mReactContextInitAsyncTask = null;
}
…
}
…
}
可以看见,这就是典型的 AsyncTask 用法,我们先关注 doInBackground 方法,onPostExecute 方法等会回头再看;doInBackground 中首先把上面封装的 ReactContextInitParams 对象里 JavaScriptExecutor.Factory 工厂对象拿到,接着调用了工厂类的 create 方法创建 JavaScriptExecutor 抽象类的实现类 JSCJavaScriptExecutor 对象(因为上面分析 recreateReactContextInBackground 方法时第一个参数传入的是 new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()))。接着往下执行了 createReactContext 方法,两个参数分别是前面封装的 ReactContextInitParams 对象中的 JSCJavaScriptExecutor 实例和 JSBundleLoader.createAssetLoader 静态方法创建的匿名内部类 JSBundleLoader 对象(热更新的话可能是另一个 Loader,参见前面分析);createReactContext 方法如下(有点长,但是句句核心啊):
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
…
//这货默认不就是上面刚刚分析的"assets://" + bundleAssetName么
mSourceUrl = jsBundleLoader.getSourceUrl();
List moduleSpecs = new ArrayList<>();
Map<Class, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();
//!!!Js层模块注册表,通过它把所有的JavaScriptModule注册到CatalystInstance。我们自定义的继承JavaScriptModule接口的Java端也是通过他来管理。
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
//ContextWrapper封装类,其实就是getApplicationContext的封装,用在ReactContext中
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
//如果是开发模式下ReactApplicationContext中有崩溃就捕获后交给mDevSupportManager处理(出错时弹个红框啥玩意的都是这货捕获的功劳)
if (mUseDeveloperSupport) {
//mDevSupportManager实例对象来源于XReactInstanceManagerImpl构造方法中一个工厂方法,实质由useDeveloperSupport决定DevSupportManager是哪个实例。
//非开发模式情况下mDevSupportManager为DisabledDev