React Native startReactApplication 方法简析

react

React Native 启动流程简析 这篇文章里,我们梳理了 RN 的启动流程,最后的 startReactApplication 由于相对复杂且涉及到最终执行前端 js 的流程,我们单独将其提取出来,独立成文加以分析。

首先来看 startReactApplication 的调用之处:

mReactRootView.startReactApplication(

getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);

可以看到是在 rootView 上调用 startReactApplication,入参为 instanceManager、appKey、mLaunchOptions

顺着 startReactApplication 扒出其调用链:mReactInstanceManager.createReactContextInBackground() -> recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()

recreateReactContextInBackgroundReactInstanceManager 中的方法,做了两件事:

  1. 创建 ReactContextInitParams 实例 initParams,如下,其入参 jsExecutorFactory 为创建 ReactInstanceManager 时传入。

    final ReactContextInitParams initParams =

    new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);

  2. 调用 runCreateReactContextOnNewThread

runCreateReactContextOnNewThreadReactInstanceManager 中的方法,主要做了两件事:

  1. 创建一个新的线程,并在新线程中通过 createReactContext 创建 ReactContext 上下文;
  2. 通过 setupReactContext 来设置上下文环境,并最终调用到 AppRegistry.js 启动App。

reactcontext">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()

builderReactInstanceManagerBuilder,我们来到该类的 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 方法创建 JSCExecutorJSCExecutorFactory 实现了 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 方法将 reactContextcatalystInstance 关联起来,并进行了一系列的为 catalystInstance 初始化的工作。最后进入到方法 catalystInstance.runJSBundle() 中。

initializeWithInstance

通过调用 getUIQueueThreadgetNativeModulesQueueThreadgetJSQueueThread创建了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);

}

)

}

这里面做的事情如下:

  1. catalystInstance.initialize(): 所有原生模块的初始化
  2. attachRootViewToInstance(reactRoot): 绘制所有的 RootView 并添加到相应实例并设置相应的监听事件
  3. 创建 UI 模块、JS 模块和原生模块线程,并设置 JS 模块和原生模块所在线程的优先级

总结本文

从 createReactContext 和 setupReactContext 两个方法的源码出发,分析了 RN startReactApplication 方法的执行过程,其中:

createReactContext 的主要作用是:创建 reactContext、创建 catalystInstance 实例,并将上述两者关联,接着通过 catalystInstance 读入 js 文件。

setupReactContext的主要作用是:初始化所有原生模块,绘制所有 rootview,创建 UI 模块、JS 模块和原生模块线程,并设置优先级。

以上是 React Native startReactApplication 方法简析 的全部内容, 来源链接: utcz.com/z/383571.html

回到顶部