在 React Native 启动流程简析 这篇文章里,我们梳理了 RN 的启动流程,最后的 startReactApplication 由于相对复杂且涉及到最终执行前端 js 的流程,我们单独将其提取出来,独立成文加以分析。
首先来看 startReactApplication 的调用之处:
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
可以看到是在 rootView 上调用 startReactApplication,入参为 instanceManager、appKey、mLaunchOptions。
顺着 startReactApplication 扒出其调用链:
mReactInstanceManager.createReactContextInBackground() -> recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()
recreateReactContextInBackground 为 ReactInstanceManager 中的方法,做了两件事:
1.创建 ReactContextInitParams 实例 initParams,如下,其入参 jsExecutorFactory 为创建 ReactInstanceManager 时传入。
final ReactContextInitParams initParams =
new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
2.调用 runCreateReactContextOnNewThread
runCreateReactContextOnNewThread 为 ReactInstanceManager 中的方法,主要做了两件事:
- 创建一个新的线程,并在新线程中通过
createReactContext创建ReactContext上下文; - 通过
setupReactContext来设置上下文环境,并最终调用到AppRegistry.js启动App。
createReactContext
先看其调用的地方:
final ReactApplicationContext reactApplicationContext =
createReactContext(
initParams.getJsExecutorFactory().create(),
initParams.getJsBundleLoader());
其两个入参分别为 JsExecutorFactory 创建的 JavaScriptExecutor 实例,和 JsBundleLoader 实例。
JavaScriptExecutor
startReactApplication 第一个入参为 getReactNativeHost().getReactInstanceManager() 获取 ReactInstanceManager 实例。ReactInstanceManager 实例在 RN 应用中只有一个,先前在创建 MainActivity 时已创建。
回顾 React Native 启动流程简析,在创建过程中实际上是调用下面的方法:
ReactInstanceManager reactInstanceManager = builder.build()
builder 即 ReactInstanceManagerBuilder,我们来到该类的 build 方法,发现其最终是执行 return new ReactInstanceManager(...),在构造参数中第 4 个参数即为:getDefaultJSExecutorFactory,来到其定义处:
private JavaScriptExecutorFactory getDefaultJSExecutorFactory(
String appName, String deviceName, Context applicationContext) {
try {
// If JSC is included, use it as normal
initializeSoLoaderIfNecessary(applicationContext);
SoLoader.loadLibrary("jscexecutor");
return new JSCExecutorFactory(appName, deviceName);
} catch (UnsatisfiedLinkError jscE) { /* ... */ }
}
也就是说在创建 ReactInstanceManagerBuilder 时我们就创建了 JSCExecutorFactory,并在随后调用其 create 方法创建 JSCExecutor 。JSCExecutorFactory 实现了 JavaScriptExecutorFactory 接口,其 create 方法如下,返回了 JSCExecutor 实例:
@Override
public JavaScriptExecutor create() throws Exception {
WritableNativeMap jscConfig = new WritableNativeMap();
jscConfig.putString("OwnerIdentity", "ReactNative");
jscConfig.putString("AppIdentity", mAppName);
jscConfig.putString("DeviceIdentity", mDeviceName);
return new JSCExecutor(jscConfig);
}
再往下看 JSCExecutor 的定义,其继承自 JavaScriptExecutor 类:
@DoNotStrip
/* package */ class JSCExecutor extends JavaScriptExecutor {
static {
SoLoader.loadLibrary("jscexecutor");
}
/* package */ JSCExecutor(ReadableNativeMap jscConfig) {
super(initHybrid(jscConfig));
}
@Override
public String getName() {
return "JSCExecutor";
}
private static native HybridData initHybrid(ReadableNativeMap jscConfig);
}
于是就很清楚了,createReactContext 第一个参数为 JSCExecutor 实例,是通过 SoLoader 加载的 C++ 模块。
JsBundleLoader
同样的,在 return new ReactInstanceManager(...),其构造参数中第 5 个参数为:JSBundleLoader.createAssetLoader(mApplication, mJSBundleAssetUrl, false)
来到其定义之处,发现其返回了 JSBundleLoader 实例,并重写了其 loadScript 方法。
public static JSBundleLoader createAssetLoader(
final Context context, final String assetUrl, final boolean loadSynchronously) {
return new JSBundleLoader() {
@Override
public String loadScript(JSBundleLoaderDelegate delegate) {
delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
return assetUrl;
}
};
}
在创建完 JSCExecutor 实例和 JSBundleLoader 实例后,正式进入到 createReactContext 方法。
createReactContext
private ReactApplicationContext createReactContext(
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
CatalystInstanceImpl.Builder catalystInstanceBuilder = /* ... */
try {
catalystInstance = catalystInstanceBuilder.build();
} finally { /* ... */ }
reactContext.initializeWithInstance(catalystInstance);
TurboModuleManager turboModuleManager =
new TurboModuleManager( /* ... */ )
catalystInstance.setTurboModuleManager(turboModuleManager);
if (mJSIModulePackage != null) {
catalystInstance.addJSIModules( /* ... */ );
}
catalystInstance.runJSBundle();
return reactContext;
在这里面,首先创建了 reactContext,并通过 catalystInstanceBuilder 创建了 catalystInstance。接着通过 initializeWithInstance 方法将 reactContext 和 catalystInstance 关联起来,并进行了一系列的为 catalystInstance 初始化的工作。最后进入到方法 catalystInstance.runJSBundle() 中。
initializeWithInstance
通过调用 getUIQueueThread、getNativeModulesQueueThread、getJSQueueThread创建了3个线程队列,分别是 UI线程、NativeModules 线程,和 JS 线程。
runJSBundle
public void runJSBundle() {
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
synchronized (mJSCallsPendingInitLock) {
mAcceptCalls = true;
for (PendingJSCall function : mJSCallsPendingInit) {
function.call(this);
}
mJSCallsPendingInit.clear();
mJSBundleHasLoaded = true;
}
Systrace.registerListener(mTraceListener);
}
通过先前返回的 mJSBundleLoader 执行其 loadScript 方法:
public String loadScript(JSBundleLoaderDelegate delegate) {
delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
return assetUrl;
}
loadScriptFromAssets 方法在 CatalystInstanceImpl 中:
public void loadScriptFromAssets(
AssetManager assetManager, String assetURL, boolean loadSynchronously) {
mSourceURL = assetURL;
jniLoadScriptFromAssets(assetManager, assetURL, loadSynchronously);
}
这里的 assetURL 是在 createAssetLoader 创建 mJSBundleLoader 时传入,其赋值时机是在 reactInstanceManagerBuilder 实例中,由 reactNativeHost 实例的 createReactInstanceManager 方法。若 开发者在 MainApplication.java 中通过重写 getJSBundleFile 方法自定义了 assetURL 则使用该 url,否则使用系统默认,如:file://sdcard/myapp_cache/index.android.bundle。
jniLoadScriptFromAssets 方法为 C++ 侧定义的方法,用于读取 js 文件。为什么 Java 代码中可以直接调用 C++ 方法,这里还要打个问号,后续在分析 Java 与 C++ 通信及 Java 与 JS 通信时阐释。
通过 createReactContext 创建了 reactContext,创建了 catalystInstance 实例,并将上述两者关联,接着通过 catalystInstance 读入 js 文件。接下来就进入到 setupReactContext 的环节。
setupReactContext
private void setupReactContext(final ReactApplicationContext reactContext) {
synchronized (mAttachedReactRoots) {
catalystInstance.initialize();
for (ReactRoot reactRoot : mAttachedReactRoots) {
if (reactRoot.getState().compareAndSet(ReactRoot.STATE_STOPPED, ReactRoot.STATE_STARTED)) {
attachRootViewToInstance(reactRoot);
}
}
}
UiThreadUtil.runOnUiThread(
public void run() {
listener.onReactContextInitialized(reactContext);
}
)
reactContext.runOnJSQueueThread(
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
)
reactContext.runOnNativeModulesQueueThread(
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
)
}
这里面做的事情如下:
- catalystInstance.initialize(): 所有原生模块的初始化
- attachRootViewToInstance(reactRoot): 绘制所有的 RootView 并添加到相应实例并设置相应的监听事件
- 创建 UI 模块、JS 模块和原生模块线程,并设置 JS 模块和原生模块所在线程的优先级
总结本文
从 createReactContext 和 setupReactContext 两个方法的源码出发,分析了 RN startReactApplication 方法的执行过程,其中:
createReactContext 的主要作用是:创建 reactContext、创建 catalystInstance 实例,并将上述两者关联,接着通过 catalystInstance 读入 js 文件。
setupReactContext的主要作用是:初始化所有原生模块,绘制所有 rootview,创建 UI 模块、JS 模块和原生模块线程,并设置优先级。
到此这篇关于React Native startReactApplication 方法简析的文章就介绍到这了,更多相关React Native startReactApplication内容请搜索NICE源码以前的文章或继续浏览下面的相关文章希望大家以后多多支持NICE源码!
