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

3,086 阅读5分钟

前言

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 and `AppRegistry.registerComponent` wasn't called.",
    );

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

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

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

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