React Native源码分析(一):环境初始化以及Bundle加载

语言: CN / TW / HK

theme: condensed-night-purple highlight: tomorrow-night-bright


前言

React Native源码分析是一个系列文章,包含三个部分, (1)初始化以及Bundle加载流程 (2)js和native的通信 (3)js组件在native侧是如何展示

打开一个RN页面

通过RN官网中的文档创建一个demo工程,在里面看一下是如何打开RN页面的

rn页面打开流程.png

从流程图上看,最终进行RN环境初始化以及打开RN页面是在ReactRootView中进行的。 在ReactRootView中有一个 ``` startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle initialProperties)

``` 方法,这里就是我们要分析的入口

流程分析

通过分析源码我认为初始化流程上有三个阶段比较重要

(1)ReactInstanceManager的创建

(2)ReactContext的创建

(3)ReactContext的装配

ReactInstanceManager的创建

ReactInstanceManager的创建在模板工程里其实是在ReactNativeHost进行进行的, ``` protected ReactInstanceManager createReactInstanceManager() { ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START); ReactInstanceManagerBuilder builder = ReactInstanceManager.builder() .setApplication(mApplication) .setJSMainModulePath(getJSMainModuleName()) .setUseDeveloperSupport(getUseDeveloperSupport()) .setDevSupportManagerFactory(getDevSupportManagerFactory()) .setRequireActivity(getShouldRequireActivity()) .setSurfaceDelegateFactory(getSurfaceDelegateFactory()) .setRedBoxHandler(getRedBoxHandler()) .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory()) .setUIImplementationProvider(getUIImplementationProvider()) .setJSIModulesPackage(getJSIModulePackage()) .setInitialLifecycleState(LifecycleState.BEFORE_CREATE) .setReactPackageTurboModuleManagerDelegateBuilder( getReactPackageTurboModuleManagerDelegateBuilder());

for (ReactPackage reactPackage : getPackages()) {
  builder.addPackage(reactPackage);
}

String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
  builder.setJSBundleFile(jsBundleFile);
} else {
  builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
ReactInstanceManager reactInstanceManager = builder.build();
ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_END);
return reactInstanceManager;

} ```

这个ReactInstanceManager是RN的一个核心类,包含了所有配置,这里我们目光聚焦到 ``` String jsBundleFile = getJSBundleFile(); if (jsBundleFile != null) { builder.setJSBundleFile(jsBundleFile); } else { builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName())); }

public ReactInstanceManagerBuilder setJSBundleFile(String jsBundleFile) {
if (jsBundleFile.startsWith("assets://")) {
  mJSBundleAssetUrl = jsBundleFile;
  mJSBundleLoader = null;
  return this;
}
return setJSBundleLoader(JSBundleLoader.createFileLoader(jsBundleFile));

}

public ReactInstanceManagerBuilder setBundleAssetName(String bundleAssetName) {
mJSBundleAssetUrl = (bundleAssetName == null ? null : "assets://" + bundleAssetName);
mJSBundleLoader = null;
return this;

}

``` jsBundleFile代表要加载的bundle路径,如果这个路径为空或者放在assets目录下,那么就会设置mJSBundleAssetUrl为bundle文件名,一般命名为(android.index.bundle),否则就会创建一个 JSBundleLoader,这个loader用来从文件加载bundle,后续在ReactContext的装配中会再提到。

ReactContext的创建

``` startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle initialProperties){ ... mReactInstanceManager.createReactContextInBackground(); ... }

``` ReactRootView的startReactApplication需要传入一个ReactInstanceManager,就是上一节构建的ReactInstanceManager。之后会调用ReactInstanceManager的createReactContextInBackground方法,顺着createReactContextInBackground方法逐级跟踪下去,发现最终会调用到ReactInstanceManager的runCreateReactContextOnNewThread(final ReactContextInitParams initParams)方法

``` @ThreadConfined(UI) private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) { ... mCreateReactContextThread = new Thread( null, new Runnable() { @Override public void run() { ... try { ... //创建ReactContext reactApplicationContext = createReactContext( initParams.getJsExecutorFactory().create(), initParams.getJsBundleLoader()); } catch (Exception e) { ... return; } ... //装在ReactContext Runnable setupReactContextRunnable = new Runnable() { @Override public void run() { try { setupReactContext(reactApplicationContext); } catch (Exception e) { ... } } };

              reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable);
              ...
            } catch (Exception e) {
              ...
            }
          }
        },
        "create_react_context");
...
mCreateReactContextThread.start();

} ```

可以看到在runCreateReactContextOnNewThread方法中会创建一个线程mCreateReactContextThread,在这个线程里会调用createReactContext进行ReactContext的创建,看一下createReactContext方法 ``` /* @return instance of {@link ReactContext} configured a {@link CatalystInstance} set / private ReactApplicationContext createReactContext( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) { ... //1.创建ReactApplicationContext实例 final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);

NativeModuleCallExceptionHandler exceptionHandler =
    mNativeModuleCallExceptionHandler != null
        ? mNativeModuleCallExceptionHandler
        : mDevSupportManager;
reactContext.setNativeModuleCallExceptionHandler(exceptionHandler);
//2.处理ReactPackage
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);

CatalystInstanceImpl.Builder catalystInstanceBuilder =
    new CatalystInstanceImpl.Builder()
        .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
        .setJSExecutor(jsExecutor)
        .setRegistry(nativeModuleRegistry)
        .setJSBundleLoader(jsBundleLoader)
        .setNativeModuleCallExceptionHandler(exceptionHandler);

... try { //3.创建CatalystInstance实例 catalystInstance = catalystInstanceBuilder.build(); } finally { ... } //4.ReactContext根据CatalystInstance进行初始化 reactContext.initializeWithInstance(catalystInstance);

...
//5执行jsbundle
catalystInstance.runJSBundle();
return reactContext;

}

这个方法主要有五个部分,首先创建ReactApplicationContext实例,也是这个方法的返回值,然后去处理ReactPackage,这里会放到下期再说,先略过,接着是创建CatalystInstance实例,CatalystInstance主要用来初始化和js通信的桥,在native侧调用js侧方法,而且在构造函数里会创建native侧调用js方法的线程,js侧调用native方法的线程,以及UI线程,他们都是MessageQueueThreadImpl类的实例,这几个线程都有一个Looper。 private CatalystInstanceImpl( final ReactQueueConfigurationSpec reactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry nativeModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) { ... //创建native线程,js线程以及ui线程 mReactQueueConfiguration = ReactQueueConfigurationImpl.create( reactQueueConfigurationSpec, new NativeExceptionHandler()); ... //初始化和js通信的桥 initializeBridge( new BridgeCallback(this), jsExecutor, mReactQueueConfiguration.getJSQueueThread(), mNativeModulesQueueThread, mNativeModuleRegistry.getJavaModules(this), mNativeModuleRegistry.getCxxModules()); ... }

```

接着调用reactContext.initializeWithInstance(catalystInstance);传入CatalystInstance到ReactContext进行配置,获取CatalystInstance中ReactQueueConfiguration的线程。

``` /* Set and initialize CatalystInstance for this Context. This should be called exactly once. / public void initializeWithInstance(CatalystInstance catalystInstance) { ... ReactQueueConfiguration queueConfig = catalystInstance.getReactQueueConfiguration(); initializeMessageQueueThreads(queueConfig); }

/* Initialize message queue threads using a ReactQueueConfiguration. / public synchronized void initializeMessageQueueThreads(ReactQueueConfiguration queueConfig) { FLog.w(TAG, "initializeMessageQueueThreads() is called."); if (mUiMessageQueueThread != null || mNativeModulesMessageQueueThread != null || mJSMessageQueueThread != null) { throw new IllegalStateException("Message queue threads already initialized"); } mUiMessageQueueThread = queueConfig.getUIQueueThread(); mNativeModulesMessageQueueThread = queueConfig.getNativeModulesQueueThread(); mJSMessageQueueThread = queueConfig.getJSQueueThread();

/** TODO(T85807990): Fail fast if any of the threads is null. */
if (mUiMessageQueueThread == null) {
  throw new IllegalStateException("UI thread is null");
}
if (mNativeModulesMessageQueueThread == null) {
  throw new IllegalStateException("NativeModules thread is null");
}
if (mJSMessageQueueThread == null) {
  throw new IllegalStateException("JavaScript thread is null");
}
mIsInitialized = true;

}

```

最后就是调用catalystInstance.runJSBundle()加载bundle

``` @Override public void runJSBundle() { ... mJSBundleLoader.loadScript(CatalystInstanceImpl.this); ... }

这里的mJSBundleLoader和ReactInstanceManager构建时传入的JSBundleLoader是一样的,我们在demo里没有传入JSBundleLoader,这里会使用默认的JSBundleLoader 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; } }; }

``` 可以看到loadScipt其实使用的就是CatalystInstanceImpl的loadScriptFromAssets方法,CatalystInstanceImpl就是CatalystInstance的实现类

``` @Override public void loadScriptFromAssets( AssetManager assetManager, String assetURL, boolean loadSynchronously) { mSourceURL = assetURL; jniLoadScriptFromAssets(assetManager, assetURL, loadSynchronously); }

``` 可以看到CatalystInstanceImpl中的loadScriptFromAssets是通过C++方法进行的bundle加载的调用,这里不再详述。至此ReactContext的创建和bundle的加载就完成了。

ReactContext的装配

ReactContext的装配是在ReactInstanceManager里进行的

``` private void setupReactContext(final ReactApplicationContext reactContext) { ... CatalystInstance catalystInstance = Assertions.assertNotNull(reactContext.getCatalystInstance());

  catalystInstance.initialize();

  mDevSupportManager.onNewReactContextCreated(reactContext);
  mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);

  ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
  for (ReactRoot reactRoot : mAttachedReactRoots) {
    if (reactRoot.getState().compareAndSet(ReactRoot.STATE_STOPPED, ReactRoot.STATE_STARTED)) {
      attachRootViewToInstance(reactRoot);
    }
  }
  ...
    UiThreadUtil.runOnUiThread(
    new Runnable() {
      @Override
      public void run() {
        moveReactContextToCurrentLifecycleState();

        for (com.facebook.react.ReactInstanceEventListener listener : finalListeners) {
          // Sometimes this listener is null - probably due to race
          // condition between allocating listeners with a certain
          // size, and getting a `final` version of the array on
          // the following line.
          if (listener != null) {
            listener.onReactContextInitialized(reactContext);
          }
        }
      }
    });
  ... 
}

``` 至此整个环境初始化过程结束,还会回调onReactContextInitialized()方法,其中还有

一个是CatalystInstance的initialize方法,这个方法可以用来实现NativeModules的初始化,然后找到创建的ReactRootView,执行它的attachRootViewToInstance ``` private void attachRootViewToInstance(final ReactRoot reactRoot) { FLog.d(ReactConstants.TAG, "ReactInstanceManager.attachRootViewToInstance()"); Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachRootViewToInstance");

@Nullable
UIManager uiManager =
    UIManagerHelper.getUIManager(mCurrentReactContext, reactRoot.getUIManagerType());

// If we can't get a UIManager something has probably gone horribly wrong
if (uiManager == null) {
  throw new IllegalStateException(
      "Unable to attach a rootView to ReactInstance when UIManager is not properly"
          + " initialized.");
}

@Nullable Bundle initialProperties = reactRoot.getAppProperties();

final int rootTag;
...
  rootTag =
      uiManager.addRootView(
          reactRoot.getRootViewGroup(),
          initialProperties == null
              ? new WritableNativeMap()
              : Arguments.fromBundle(initialProperties),
          reactRoot.getInitialUITemplate());
  reactRoot.setRootViewTag(rootTag);
  reactRoot.runApplication();
...

}

```

最终会调用到ReactRootView的runApplication方法

```

public void runApplication() { try { if (mReactInstanceManager == null || !mIsAttachedToInstance) { return; }

  ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
  if (reactContext == null) {
    return;
  }

  CatalystInstance catalystInstance = reactContext.getCatalystInstance();
  String jsAppModuleName = getJSModuleName();

  if (mWasMeasured) {
    updateRootLayoutSpecs(true, mWidthMeasureSpec, mHeightMeasureSpec);
  }

  WritableNativeMap appParams = new WritableNativeMap();
  appParams.putDouble("rootTag", getRootViewTag());
  @Nullable Bundle appProperties = getAppProperties();
  if (appProperties != null) {
    appParams.putMap("initialProps", Arguments.fromBundle(appProperties));
  }

  mShouldLogContentAppeared = true;

  catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
} finally {
  Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}

}

```

CatalystInstance会通过动态代理调用声明AppRegistry接口中的方法,最终该runApplication就会映射调用到js侧代码的同名函数

`` // AppRegistry.js runApplication( appKey: string, appParameters: any, displayMode?: number, ): void { if (appKey !== 'LogBox') { const logParams = __DEV__ ? '" with ' + JSON.stringify(appParameters) : ''; const msg = 'Running "' + appKey + logParams; infoLog(msg); BugReporting.addSource( 'AppRegistry.runApplication' + runCount++, () => msg, ); } invariant( runnables[appKey] && runnables[appKey].run,"${appKey}" has not been registered. This can happen if:\n+ '* Metro (the local dev server) is run from the wrong folder. ' + 'Check if Metro is running, stop it and restart it in the current project.\n' + "* A module failed to load due to an error andAppRegistry.registerComponent` wasn't called.", );

SceneTracker.setActiveScene({name: appKey});
runnables[appKey].run(appParameters, displayMode);

},

``` 执行AppRegistery的renderApplication方法进行组件的渲染。

关注我的公众号:滑板上的老砒霜

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿