0%

ReactNative源码-启动过程

概述

就像大多数框架一样,ReactNative(简称RN)启动需要初始化依赖的环境和资源,抛开繁复的细节,概括为以下几点:

  1. 初始化通信接口表,表中保存 java 端和 js 端的接口信息。
  2. 初始化ReactBridge,作为 jsjava 通信的桥梁,在 java 端开启两个线程 native_modulesjs ,加上 ui 线程,总共三个线程。
  3. 加载JSbundle文件

流程图

画了个流程图,如下

rn_start_01.svg-325.3kB

几个重要类

在开始之前,先看几个重要的类,有个印象,后面看起来会轻松很多。

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
//ReactRootViewRoot
public void startReactApplication(ReactInstanceManager mReactInstanceManager,String moduleName, Bundle launchOptions) {
//...
//初始化Rn环境
mReactInstanceManager.createReactContextInBackground();
//初始化UI,由于各种状态判定,第一次启动时,这句话不会执行
//mReactInstanceManager.attachMeasuredRootView(this);
}

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 函数干了两件事

  1. 创建一个ReactContextInitParams对象,包含之前创建的jsExecutorFactory, jsBundleLoader两个对象
  2. 将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()));
}
  1. 回调之前创建的getJsExecutorFactory.create方法,传入初始化参数(默认为 null ),初始化native
  2. 执行核心初始化函数 createReactContext

step_7 核心初始化函数

这步很重要,基本上80%的初始化都在这里,大概分为四部分:

  1. 加载java和js的接口接口。
  2. 初始化通信表
  3. 初始化RN 环境
  4. 加载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);
//1. 加载java和js接口
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;
//2 初始化配置
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModulesConfig(javaScriptModulesConfig)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
CatalystInstance catalystInstance= catalystInstanceBuilder.build();
//3 初始化RN 环境
reactContext.initializeWithInstance(catalystInstance);
//4 加载bundle文件
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

加载完接口之后,去干第二件事,初始化通信环境。这一步做了几件事:

  1. 实例化CatalystInstanceImpl, 存储java和js接口
  2. 实例化jni通信模块ReactBridge将java和js接口组织成json形式,通过jni传给native,native在传到js中去。
  3. 创建两个后台线程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);