概述
就像大多数框架一样,ReactNative(简称RN)启动需要初始化依赖的环境和资源,抛开繁复的细节,概括为以下几点:
- 初始化通信接口表,表中保存 java 端和 js 端的接口信息。
- 初始化ReactBridge,作为 js 和 java 通信的桥梁,在 java 端开启两个线程 native_modules 和 js ,加上 ui 线程,总共三个线程。
- 加载JSbundle文件
流程图
画了个流程图,如下
几个重要类
在开始之前,先看几个重要的类,有个印象,后面看起来会轻松很多。
class |
作用 |
ReactInstanceManager |
1.创建ReactContext,CatalystInstance等类 2. 解析package生成注册表 3. 配合ReactRootView管理View的创建,生命周期等功能 4. 默认实现类为 ReactInstanceManagerImpl |
ReactContext |
RN程序自己的上下文,里面有CatalystInstance实例,可以去获得Java和JS的module。 |
ReactRootView |
RN程序的根视图,Rn的入口 |
CatalystInstance |
保存有js和java端的接口,内部通过ReactBridge进行通信,进行方法调用 |
ReactBridge |
和jni进行通信 |
NativeModuleRegistry |
Java接口的注册表 |
JavascriptModuleRegistry |
JS接口的注册表 |
CoreModulePackage |
提供RN核心的Java接口和js接口 |
MainReactPackage |
RN帮我们封装的类,提供一些额外的Java接口和js接口,用来加载自己的接口 |
JsBundleLoader |
故名思议,用于加载打包成bundle的js代码 |
步骤分析
step_1
按照官方的示例,入口调用为 reactRootView.startReactApplication(…) ,看看这句代码干了什么
1 2 3 4 5 6 7 8
| public void startReactApplication(ReactInstanceManager mReactInstanceManager,String moduleName, Bundle launchOptions) { mReactInstanceManager.createReactContextInBackground(); }
|
mReactInstanceManager 的类型为 ReactInstanceManager,这是一个抽象类,真正的实现类是 ReactInstanceManagerImpl
step_2
接着看 ReactInstanceManagerImpl 的 createReactContextInBackground方法:
1 2 3 4
| public void createReactContextInBackground() { mHasStartedCreatingInitialContext = true; recreateReactContextInBackgroundInner(); }
|
recreateReactContextInBackgroundInner会继续往下进行方法调用,忽略dev模式代码以及异常判断,最终会调用到 recreateReactContextInBackgroundFromBundleFile,
1 2 3 4 5
| private void recreateReactContextInBackgroundFromBundleFile() { recreateReactContextInBackground( new JSCJavaScriptExecutor.Factory(), JSBundleLoader.createFileLoader(mApplicationContext, mJSBundleFile)); }
|
这个函数创建了两个对象
1.JSBundleLoader 提供jsBundle文件的路径,
2. 创建一个JavaScriptExecutor.Factory对象,用于初始化js端环境
step_3
recreateReactContextInBackground 函数干了两件事
- 创建一个ReactContextInitParams对象,包含之前创建的jsExecutorFactory, jsBundleLoader两个对象
- 将initParams传给ReactContextInitAsyncTask,启动asyncTask,开始初始化Rn所需要的资源,
跳过step_4 – step_5
step_6
AsyncTask… 这是一道送分题啊同学们,瞅瞅干了啥
1 2 3 4 5
| protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) { JavaScriptExecutor jsExecutor =params[0].getJsExecutorFactory().create( mJSCConfig == null ? new WritableNativeMap() :mJSCConfig.getConfigMap()); return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader())); }
|
- 回调之前创建的getJsExecutorFactory.create方法,传入初始化参数(默认为 null ),初始化native
- 执行核心初始化函数 createReactContext
step_7 核心初始化函数
这步很重要,基本上80%的初始化都在这里,大概分为四部分:
- 加载java和js的接口接口。
- 初始化通信表
- 初始化RN 环境
- 加载bundle文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| private ReactApplicationContext createReactContext(JavaScriptExecutor jsExecutor,JSBundleLoader jsBundleLoader) { mSourceUrl = jsBundleLoader.getSourceUrl(); NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder(); avaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder(); ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext); CoreModulesPackage coreModulesPackage =new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider); processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder); for (ReactPackage reactPackage : mPackages) { processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder); } NativeModuleRegistry nativeModuleRegistry = nativeRegistryBuilder.build(); JavaScriptModulesConfig javaScriptModulesConfig=jsModulesBuilder.build();
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null? mNativeModuleCallExceptionHandler:mDevSupportManager; CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder() .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()) .setJSExecutor(jsExecutor) .setRegistry(nativeModuleRegistry) .setJSModulesConfig(javaScriptModulesConfig) .setJSBundleLoader(jsBundleLoader) .setNativeModuleCallExceptionHandler(exceptionHandler); CatalystInstance catalystInstance= catalystInstanceBuilder.build(); reactContext.initializeWithInstance(catalystInstance); catalystInstance.runJSBundle(); return reactContext; }
|
在第8行和第10行分别调用了processPackage方法,唯一的区别是传入的第二个参数不同。参数的类型是ReactPackager,这是一个接口类,用于提供js和java接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| //ReactPackage.java public interface ReactPackage {
/** * 返回java端暴露给js的接口集合 */ List<NativeModule> createNativeModules(ReactApplicationContext reactContext); /** * @return 返回js端暴露给JAVA端的接口集合 */ List<Class<? extends JavaScriptModule>> createJSModules();
/** * @return 返回给js端native封装的view集合 */ List<ViewManager> createViewManagers(ReactApplicationContext reactContext); }
|
接着看processPackage方法,两次调用分别传入了 coreModulesPackage 对象和 reactPackage对象,coreModules提供RN必须的一些通信接口如UIManagerModule和RCTEventEmitter等等,而reactPackage在ReactRootView.startReactApplication()则是我们自己定义的类对象,加载自定义的接口。RN的样例代码中为我们封装了一个类MainReactPackage,加载DialogModule、IntentModule、ReactVirtualTextViewManager等等。
回头继续看processPackage方法实现,也很简单,就是提取ReactPackager中提供的java和js接口并保存在对应的对象中
1 2 3 4 5 6 7 8 9 10
| private void processPackage(ReactPackage reactPackage, ReactApplicationContext reactContext, NativeModuleRegistry.Builder nativeRegistryBuilder, JavaScriptModulesConfig.Builder jsModulesBuilder) { for (NativeModule nativeModule : reactPackage.createNativeModules(reactContext)) { nativeRegistryBuilder.add(nativeModule); } for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) { jsModulesBuilder.add(jsModuleClass); } }
|
数据准备好之后, 会调用 nativeRegistryBuilder.build() 和 jsModulesBuilder.build() 构造对象,以nativeRegistryBuilder为例:
1 2 3 4 5 6 7 8 9 10 11
| public NativeModuleRegistry build() { List<ModuleDefinition> moduleTable = new ArrayList<>(); Map<Class<? extends NativeModule>, NativeModule> moduleInstances = new HashMap<>(); int idx = 0; for (NativeModule module : mModules.values()) { ModuleDefinition moduleDef = new ModuleDefinition(idx++, module.getName(), module); moduleTable.add(moduleDef); moduleInstances.put(module.getClass(), module); } return new NativeModuleRegistry(moduleTable, moduleInstances); }
|
代码逻辑很清晰,就是构造一张java接口表,表中保存java的modelId,mode名称,以及model的引用。举个栗子:
id |
modelName |
model |
1 |
DialogManagerAndroid |
DialogModule |
2 |
IntentAndroid |
IntentModule |
加载完接口之后,去干第二件事,初始化通信环境。这一步做了几件事:
- 实例化CatalystInstanceImpl, 存储java和js接口
- 实例化jni通信模块ReactBridge将java和js接口组织成json形式,通过jni传给native,native在传到js中去。
- 创建两个后台线程mqt_js和mqt_native_modules,线程中使用Looper进行消息循环,在这两个线程和ui线程中各创建一个handler,保存handler的引用到对应线程中去。对应关系如下
线程 |
looper |
handler |
ui线程 |
true |
handler_main_ui |
js线程 |
true |
handler_mqt_js |
native_modules线程 |
true |
handler_native_modules |
下面看具体的代码流程
首先,第25行build函数实际上是实例化CatalystInstanceImpl
1 2 3 4 5 6 7 8 9
| public CatalystInstanceImpl build() { return new CatalystInstanceImpl( mReactQueueConfigurationSpec, mJSExecutor, mRegistry, mJSModulesConfig, mJSBundleLoader, mNativeModuleCallExceptionHandler); }
|
继续看CatalystInstanceImpl的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| private CatalystInstanceImpl( final ReactQueueConfigurationSpec ReactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry registry, final JavaScriptModulesConfig jsModulesConfig, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) { //1 创建2个线程和三个handler mReactQueueConfiguration = ReactQueueConfigurationImpl.create(ReactQueueConfigurationSpec, new NativeExceptionHandler()); mBridgeIdleListeners = new CopyOnWriteArrayList<>(); mJavaRegistry = registry; mJSModuleRegistry = new JavaScriptModuleRegistry(CatalystInstanceImpl.this, jsModulesConfig); mJSBundleLoader = jsBundleLoader; mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; mTraceListener = new JSProfilerTraceListener(); mBridge = mReactQueueConfiguration.getJSQueueThread().callOnQueue( new Callable<ReactBridge>() { @Override public ReactBridge call() throws Exception { //2 实例化和jni通信的ReactBridge,并将Java端和js端的方法组装成json形式,传递到C++层 return initializeBridge(jsExecutor, jsModulesConfig); } }).get(); }
|
看第9行代码,调用create方法,创建了三个线程的handler,并保存在对应线程的ThreadLocal中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public static ReactQueueConfigurationImpl create(ReactQueueConfigurationSpec spec, QueueThreadExceptionHandler exceptionHandler) { Map<MessageQueueThreadSpec, MessageQueueThreadImpl> specsToThreads = MapBuilder.newHashMap();
MessageQueueThreadSpec uiThreadSpec = MessageQueueThreadSpec.mainThreadSpec(); //创建一个名为"main-ui"的handler,保存在UI线程中ThreadLocal中 MessageQueueThreadImpl uiThread =MessageQueueThreadImpl.create(uiThreadSpec, exceptionHandler); specsToThreads.put(uiThreadSpec, uiThread);
MessageQueueThreadImpl jsThread = specsToThreads.get(spec.getJSQueueThreadSpec()); if (jsThread == null) { //创建一个名称为mqt_js 的线程, // 并将Js线程中handler保存到js线程中的ThreadLocal中 //线程中使用Lopper轮询消息,由handle人处理消息 jsThread = MessageQueueThreadImpl.create(spec.getJSQueueThreadSpec(), exceptionHandler); }
MessageQueueThreadImpl nativeModulesThread = specsToThreads.get(spec.getNativeModulesQueueThreadSpec()); if (nativeModulesThread == null) { //创建一个名称为mqt_native_modules 的线程, // 并将Js线程中handler保存到mqt_native_modules线程中的ThreadLocal中 //线程中使用Lopper轮询消息,由handler人处理消息 nativeModulesThread =MessageQueueThreadImpl.create(spec.getNativeModulesQueueThreadSpec(), exceptionHandler); } return new ReactQueueConfigurationImpl(uiThread, nativeModulesThread, jsThread); }
|
这步中比较重要的参数是 spec 保存两个类型为MessageQueueThreadSpec的数据结构,同时,在第5行创建了一个新的MessageQueueThreadSpec,共三个对象
对象 |
name |
ThreadType |
uiThreadSpec |
main_ui |
MAIN_UI |
mNativeModulesQueueThreadSpec |
native_modules |
NEW_BACKGROUND |
mJSQueueThreadSpec |
js |
NEW_BACKGROUND |
分别将这三个对象进行 MessageQueueThreadImpl.create 操作,NEW_BACKGROUND 类型的会常见新的线程和handler,MAIN_UI的则只会创建ui线程的handler,最后将这三个handler保存在各自线程的ThreadLocal中,这部分代码就不贴出来了。 |
|
|
初始化两个线程和三个handler后,调用 initializeBridge函数,初始化ReactBridge,将模块和函数表传到jni中去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| private ReactBridge initializeBridge(JavaScriptExecutor jsExecutor, JavaScriptModulesConfig jsModulesConfig) { ReactBridge bridge = new ReactBridge( jsExecutor, new NativeModulesReactCallback(), mReactQueueConfiguration.getNativeModulesQueueThread()); mMainExecutorToken = bridge.getMainExecutorToken(); bridge.setGlobalVariable( "__fbBatchedBridgeConfig", buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig)); bridge.setGlobalVariable( "__RCTProfileIsProfiling", Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false"); mJavaRegistry.notifyReactBridgeInitialized(bridge); return bridge; }
|
主要看这9行代码,buildModulesConfigJSONProperty函数,将模块方法组装成json传到Jni中去,截取了函数Json返回值的一部分,是长这样的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| { "remoteModuleConfig": { "Log": { "moduleID": 1, "supportsWebWorkers": false, "methods": { "d": { "methodID": 0, "type": "remote" }, "i": { "methodID": 1, "type": "remote" }, "w": { "methodID": 2, "type": "remote" }, "e": { "methodID": 3, "type": "remote" } } }, "NetInfo": { "moduleID": 2, "supportsWebWorkers": false, "methods": { "getCurrentConnectivity": { "methodID": 0, "type": "remoteAsync" }, "isConnectionMetered": { "methodID": 1, "type": "remoteAsync" } } },
"ToastAndroid": { "moduleID": 19, "supportsWebWorkers": false, "methods": { "show": { "methodID": 0, "type": "remote" } }, "constants": { "LONG": 1, "SHORT": 0 } }, } }
|
第四步 在reactContext中保持三个线程handler的引用
1 2 3 4 5 6 7 8 9
| reactContext.initializeWithInstance(catalystInstance); //ReactContext.java public void initializeWithInstance(CatalystInstance catalystInstance) { mCatalystInstance = catalystInstance; ReactQueueConfiguration queueConfig = catalystInstance.getReactQueueConfiguration(); mUiMessageQueueThread = queueConfig.getUIQueueThread(); mNativeModulesMessageQueueThread = queueConfig.getNativeModulesQueueThread(); mJSMessageQueueThread = queueConfig.getJSQueueThread(); }
|
第五步 通过jni,native开始加载jsBundle
1 2 3 4 5 6 7 8 9 10 11
| catalystInstance.runJSBundle(); //CatalystInstanceImpl.java public void runJSBundle() { mJSBundleHasLoaded = mReactQueueConfiguration.getJSQueueThread().callOnQueue(new Callable<Boolean>() { @Override public Boolean call() throws Exception { mJSBundleLoader.loadScript(mBridge); return true; } }).get(); }
|
1 2 3 4 5 6 7 8 9
| mJSBundleLoader.loadScript(mBridge); //JSBundleLoader.java public void loadScript(ReactBridge bridge) { if (fileName.startsWith("assets://")) { bridge.loadScriptFromAssets(context.getAssets(), fileName.replaceFirst("assets://", "")); } else { bridge.loadScriptFromFile(fileName, "file://" + fileName); } }
|
以loadScriptFromFile都是native方法,将bundle路径传到native,native加载JS代码和资源
1 2 3
| public native void loadScriptFromAssets(AssetManager assetManager, String assetName);
public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL);
|