51

从 Hello World 看 RN 的启动流程(一) · Issue #75 · dwqs/blog · GitHub

 4 years ago
source link: https://github.com/dwqs/blog/issues/75?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

第一次接触 React Native 是在四年前实习的时候,当时在项目中使用的 RN 版本是 0.28.x,间隔四年之后,再次在项目中使用 RN 时版本已是 0.57.x。在撰写本文时,RN 的版本是 0.60.4,所以,本文将以 0.60.4 版本为基础,简要分析 RN 应用在 Android 平台上的启动流程

Hello World

用 RN 来写一个 Hello World 应用非常简单。通过 RN cli 生成项目之后,更改程序的入口文件即可:

import { Text, AppRegistry } from 'react-native'
import App from './App'

// ...
AppRegistry.registerComponent('HelloWorld', () => App);

程序的入口文件一般是通过 AppRegistry 对 RN 应用进行注册,registerComponent 的实现如下:

// react-native/Libraries/ReactNative/AppRegistry.js

// ...
// 类型声明
export type ComponentProvider = () => React$ComponentType<any>;
export type Runnable = {
  component?: ComponentProvider,
  run: Function,
};
export type Runnables = {
  [appKey: string]: Runnable,
};

// 保存注册过的应用
const runnables: Runnables = {};

// ...
// 注册应用的根组件
registerComponent(
    appKey: string,
    componentProvider: ComponentProvider,
    section?: boolean,
  ): string {
    
    runnables[appKey] = {
      componentProvider,
      /** 
      * appParameters 是原生端初始化 RN 应用时透传的参数
      * 属性主要包含用于初始化的 initialProps,rootTag,fabric等
      */
      run: appParameters => {
        renderApplication(
          // ...
        )
      },
    };
    
    // ...
    return appKey;
  },
// ...

registerComponent 方法的第一个参数是 appKey,第二个参数是与之对应的根组件。因而如果要注册多个 RN 应用,就需要确保 appKey 的值是唯一的,因为原生端也是依赖 appKey 来启动对应的 RN 应用的。

那么,在 JavaScript 端注册 HelloWorld 应用之后,原生端是怎么启动 HelloWorld 的呢?

通过 RN cli 新建 Hello 项目之后,不仅会生成 JavaScript 代码,也会创建与之对应的原生工程项目。对于 Android 工程而言,会生成两个主要的类: MainActivityMainApplication,这是 Android 应用的启动入口。

先看下 MainActivity 的实现:

// ...
public class MainActivity extends ReactActivity {

  @Override
  protected String getMainComponentName() {
    return "HelloWorld";
  }
}

MainActivity 继承了 ReactActivity,并重写了 getMainComponentName 方法,而这个方法就是返回在 JavaScript 端注册的 appKey

然后再看下 MainApplication 的实现:

// ...
public class MainApplication extends Application implements ReactApplication {
  // 创建 ReactNativeHost 的匿名子类实例
  private final ReactNativeHost mReactNativeHost =
      new ReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
          // 是否开启 dev mode
          return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          /**
          * 返回原生依赖的包列表
          * 实际开发中,js 端会对原生端有依赖,当原生端把依赖暴露给 RN 时,需要在这手动添加原生包
          * 在后续的流程中,会把所有的原生依赖在 C++ 层进行注册,供 js 端调用
          * packages.add(new MyReactNativePackage())
          */
          return packages;
        }

        @Override
        protected String getJSMainModuleName() {
          // 返回主模块名,默认为 index
          return "index";
        }
      };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    /**
    * 初始化 Soloader(https://github.com/facebook/SoLoader)
    * 在一个 RN 应用中,牵涉到 js 和 Native(C/C++) 的通信(JSC/JSI),Java 和 Native(C/C++) 的通信(JNI)
    * 以及 js 和 Java 之间的通信(转嫁到 C++ 上)
    * 而 Soloader 就是一个 Native Code(主要是c/c++) 的 loader,用于加载和解析动态链接库,为后续的通信作准备
    */
    SoLoader.init(this, /* native exopackage */ false);
  }
}

MainApplication 实现了接口 ReactApplication,主要是实现其 getReactNativeHost 方法:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactApplication.java

package com.facebook.react;

public interface ReactApplication {

  /** Get the default {@link ReactNativeHost} for this app. */
  ReactNativeHost getReactNativeHost();
}

ReactNativeHost 是 RN 应用的宿主类,其本身是一个抽象类,在创建 ReactNativeHost 实例时,重写了里面的两个抽象方法:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java

// ...
public abstract class ReactNativeHost {

  private final Application mApplication;
  private @Nullable ReactInstanceManager mReactInstanceManager;

  protected ReactNativeHost(Application application) {
    // 保存 app 应用实例
    mApplication = application;
  }

  /** 
  * 返回 ReactInstanceManager 实例
  * 这个方法会在 ReactDelegate#loadApp 方法中被调用
  * 其返回值赋值给 ReactRootView#startReactApplication 方法的第一个参数
  */
  public ReactInstanceManager getReactInstanceManager() {
    if (mReactInstanceManager == null) {
      // 略去日志代码
      // 实例不存在则创建实例
      mReactInstanceManager = createReactInstanceManager();
      // 略去日志代码
    }
    return mReactInstanceManager;
  }
  
  // ...
 
  // 创建 ReactInstanceManager 实例
  protected ReactInstanceManager createReactInstanceManager() {
    ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
    /**
    * 类 ReactInstanceManagerBuilder 提供了很多 set 方法来保存创建
    * ReactInstanceManager 实例相关的上下文信息
    */
    ReactInstanceManagerBuilder builder =
        ReactInstanceManager.builder()
            // 应用上下文
            .setApplication(mApplication)
            // 设置MainModuleName,相当于应用首页的 js bundle
            .setJSMainModulePath(getJSMainModuleName())
            // 是否开启dev模式
            .setUseDeveloperSupport(getUseDeveloperSupport())
            // 设置红盒的回调(dev 模式下才有红屏显示)
            .setRedBoxHandler(getRedBoxHandler())
            // 设置 js 的执行器工厂实例,为后续加载和解析 js bundle 作准备
            .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
            // 设置UI实现机制的Provider
            .setUIImplementationProvider(getUIImplementationProvider())
            // 设置 JSI Module:js -->C++(JSI)-->Java Module
            .setJSIModulesPackage(getJSIModulePackage())
            // 初始化 host 的生命周期状态
            .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
    
    // 添加原生依赖包
    for (ReactPackage reactPackage : getPackages()) {
      builder.addPackage(reactPackage);
    }
    
    /** 
    * 获取js Bundle的加载路径
    * 如果没有自定义加载路径,就从 Android assets 中加载 index.android.bundle 文件
    * 默认路径是:assets:// + getBundleAssetName 方法的返回值
    */
    String jsBundleFile = getJSBundleFile();
    if (jsBundleFile != null) {
      builder.setJSBundleFile(jsBundleFile);
    } else {
      builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
    }
    
    // 根据上面设置的一系列参数去创建 ReactInstanceManager 实例
    ReactInstanceManager reactInstanceManager = builder.build();
    ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_END);
    return reactInstanceManager;
  }

  // RedBox 相关的回调
  protected @Nullable RedBoxHandler getRedBoxHandler() {
    return null;
  }

  // 如果要自定义 js 执行器,重写该方法
  protected @Nullable JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
    return null;
  }

  protected final Application getApplication() {
    return mApplication;
  }

  // 如果需要自定义UI实现机制,可重写该方法,但99%的情况下不需要,使用默认机制就行
  protected UIImplementationProvider getUIImplementationProvider() {
    return new UIImplementationProvider();
  }

  protected @Nullable JSIModulePackage getJSIModulePackage() {
    return null;
  }


  /** 
  * 返回主模块名
  * 返回值决定了从服务器拉取 js bundle 的 URL,仅在开发模式下有用
  */    
  protected String getJSMainModuleName() {
    return "index.android";
  }
  
  
  /** 
  * 返回 js bundle 文件的路径
  * 如果需要自定义路径,需要在子类中重写这个方法
  * 默认会从 Android assets 中加载 js bundle
  * 文件路径类似于 "file://sdcard/myapp_cache/index.android.bundle"
  */
  protected @Nullable String getJSBundleFile() {
    return null;
  }
  
  // 返回 Android assets 中的 js bundle 名称
  protected @Nullable String getBundleAssetName() {
    return "index.android.bundle";
  }
  
  // 是否开启dev模式
  public abstract boolean getUseDeveloperSupport();

  // 返回app需要的ReactPackage,包含了运行时需要用到的NativeModule/JavaScriptModule等
  protected abstract List<ReactPackage> getPackages();
}

虽然在 ReactDelegate#loadApp 方法被调用时才创建 ReactInstanceManager 实例,但我们简单看下其创建的参数列表,这有利于后文的理解。

ReactInstanceManagerBuilder#build 方法的实现如下:

// react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java

// ...

import static com.facebook.react.modules.systeminfo.AndroidInfoHelpers.getFriendlyDeviceName;

// ...

public ReactInstanceManager build() {
    // ...
    if (mUIImplementationProvider == null) {
      // create default UIImplementationProvider if the provided one is null.
      mUIImplementationProvider = new UIImplementationProvider();
    }
    
    // We use the name of the device and the app for debugging & metrics
    String appName = mApplication.getPackageName();
    String deviceName = getFriendlyDeviceName();
    
    return new ReactInstanceManager(
        mApplication,
        mCurrentActivity,
        mDefaultHardwareBackBtnHandler,
        mJavaScriptExecutorFactory == null
            ? getDefaultJSExecutorFactory(appName, deviceName)
            : mJavaScriptExecutorFactory,
        (mJSBundleLoader == null && mJSBundleAssetUrl != null)
            ? JSBundleLoader.createAssetLoader(
                mApplication, mJSBundleAssetUrl, false /*Asynchronous*/)
            : mJSBundleLoader,
        mJSMainModulePath,
        mPackages,
        mUseDeveloperSupport,
        mBridgeIdleDebugListener,
        Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),
        mUIImplementationProvider,
        mNativeModuleCallExceptionHandler,
        mRedBoxHandler,
        mLazyViewManagersEnabled,
        mDevBundleDownloadListener,
        mMinNumShakes,
        mMinTimeLeftInFrameForNonBatchedOperationMs,
        mJSIModulesPackage,
        mCustomPackagerCommandHandlers);
}

private JavaScriptExecutorFactory getDefaultJSExecutorFactory(String appName, String deviceName) {
    try {
      // 加载 C++ 层的 jscexecutor,优先考虑使用 JSC 引擎
      SoLoader.loadLibrary("jscexecutor");
      return new JSCExecutorFactory(appName, deviceName);
    } catch (UnsatisfiedLinkError jscE) {
      // JSC 加载失败,就使用 Hermes 引擎
      return new HermesExecutorFactory();
    }
  }
// ...

重点需要关注两点:

  1. mJSBundleLoader 的初始化:JSBundleLoader 是一个抽象类,但根据不同的 bundle 加载场景,提供了不同的静态方法来创建匿名子类实例。上文说到,可以通过重写 ReactNativeHost#getJSBundleFile 方法自定义 bundle 的加载路径。如果 bundle 路径不是以 assets:// 开头,则会通过 JSBundleLoader 类的静态方法 createFileLoader 创建一个实例;反之,则会通过其静态方法 createAssetLoader 创建一个实例。

  2. mJavaScriptExecutorFactory 的初始化:通过上文可知,ReactNativeHost#getJavaScriptExecutorFactory 返回值是 null,因而会调用 getDefaultJSExecutorFactory 方法创建默认的 js 执行器工厂实例。

接下来看看 ReactActivity 的实现。

ReactActivity

从上文可知,MainActivity 继承了 ReactActivity,并重写了 getMainComponentName 方法。ReactActivity 是一个抽象类,其主要实现如下:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivity.java

// ...
public abstract class ReactActivity extends AppCompatActivity
    implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
 
  // 声明 ReactActivityDelegate 实例变量
  private final ReactActivityDelegate mDelegate;

  protected ReactActivity() {
    // 在构造函数中创建 ReactActivityDelegate 实例
    mDelegate = createReactActivityDelegate();
  }
  
  // 在子类 MainActivity 中重写了该方法,返回 js 端注册的 appKey
  protected @Nullable String getMainComponentName() {
    return null;
  }

  protected ReactActivityDelegate createReactActivityDelegate() {
    // 创建 ReactActivityDelegate 实例
    return new ReactActivityDelegate(this, getMainComponentName());
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mDelegate.onCreate(savedInstanceState);
  }
  
  //...
  
  // 获取 ReactNativeHost 实例
  protected final ReactNativeHost getReactNativeHost() {
    return mDelegate.getReactNativeHost();
  }
  
  // 获取 ReactInstanceManager 实例    
  protected final ReactInstanceManager getReactInstanceManager() {
    return mDelegate.getReactInstanceManager();
  }
  
  // 加载 RN App
  protected final void loadApp(String appKey) {
    mDelegate.loadApp(appKey);
  }
}

ReactActivity 本身是一个抽象类,没有具体的功能实现,其主要作用有两个:

  1. 提供 getMainComponentName 方法的声明
  2. 创建 ReactActivityDelegate 实例,便于把具体的功能全委托给 ReactActivityDelegate 类来处理

ReactActivityDelegate

ReactActivityDelegate 类的主要实现如下:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java

// ...
public class ReactActivityDelegate {
  private final @Nullable Activity mActivity;
  private final @Nullable String mMainComponentName;
  // ...
  private ReactDelegate mReactDelegate;
  
  // 构造函数
  public ReactActivityDelegate(ReactActivity activity, @Nullable String mainComponentName) {
    mActivity = activity;
    mMainComponentName = mainComponentName;
  }
  
  /**
  * 获取 bundle 初始化的 props
  * 如果需要初始化的 props,需要 Android 实现一个子类并重写这个方法或者自定义实现 ReactActivityDelegate
  * 该方法的返回值会赋值给 js 端 run 方法的参数appParameters#initialProps
  */
  protected @Nullable Bundle getLaunchOptions() {
    return null;
  }
    
  // 获取 ReactRootView 实例 
  protected ReactRootView createRootView() {
    return mReactDelegate.createRootView();
  }
  
  /**
  * 获取当前应用使用的 ReactNativeHost 实例
  * 上文有说到,在 ReactNativeHost 的实例是在 MainApplication 中创建的 
  */    
  protected ReactNativeHost getReactNativeHost() {
    return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
  }
  
  // 获取 ReactInstanceManager 实例
  public ReactInstanceManager getReactInstanceManager() {
    return mReactDelegate.getReactInstanceManager();
  }
  
  // 获取 appKey    
  public String getMainComponentName() {
    return mMainComponentName;
  }

  protected void onCreate(Bundle savedInstanceState) {
    String mainComponentName = getMainComponentName();
    // 创建 ReactDelegate 实例
    mReactDelegate =
        new ReactDelegate(
            getPlainActivity(), getReactNativeHost(), mainComponentName, getLaunchOptions());
    if (mMainComponentName != null) {
      // 开启加载应用
      loadApp(mainComponentName);
    }
  }

  protected void loadApp(String appKey) {
    // 实际是调用 ReactDelegate 实例的 loadApp 方法启动 RN 应用
    mReactDelegate.loadApp(appKey);
    // 调用Activity 的setContentView()方法,将根视图添加到当前的Activity
    getPlainActivity().setContentView(mReactDelegate.getReactRootView());
  }
  
  // ...
  
  protected Context getContext() {
    return Assertions.assertNotNull(mActivity);
  }

  protected Activity getPlainActivity() {
    return ((Activity) getContext());
  }
}

ReactActivityDelegate 类主要是在其生命周期的 onCreate 方法中做了三件事:创建 ReactDelegate 实例、调用 ReactActivityDelegate#loadApp 开始启动 RN 应用以及设置当前 Activity 的根视图。

ReactDelegate

ReactDelegate 类的实现比较简单,关键部分代码如下:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactDelegate.java

// ...
public class ReactDelegate {
    
  // ...
  
  // 构造函数
  public ReactDelegate(
      Activity activity,
      ReactNativeHost reactNativeHost,
      @Nullable String appKey,
      @Nullable Bundle launchOptions) {
    mActivity = activity;
    mMainComponentName = appKey;
    mLaunchOptions = launchOptions;
    mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
    mReactNativeHost = reactNativeHost;
  }
  
  // ...

  public void loadApp(String appKey) {
    if (mReactRootView != null) {
      throw new IllegalStateException("Cannot loadApp while app is already running.");
    }
    // 创建 RN 容器根视图
    mReactRootView = createRootView();
    mReactRootView.startReactApplication(
        // 这里会去创建 ReactInstanceManager 实例
        getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
  }

  public ReactRootView getReactRootView() {
    return mReactRootView;
  }

  protected ReactRootView createRootView() {
    return new ReactRootView(mActivity);
  }
   
  // ...
  
  private ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  public ReactInstanceManager getReactInstanceManager() {
    return getReactNativeHost().getReactInstanceManager();
  }
}

ReactDelegate#loadApp 方法主要做了两件事:创建 RootView 和开始启动 React App。

ReactRootView

ReactRootView 是一个自定义的 View,其父类是 FrameLayout,代码量比较大,因而我们只顺着上面的思路看一下 startReactApplication 的实现:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java

// ...
public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
  // ...
  public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle initialProperties,
      @Nullable String initialUITemplate) {
    // ...
    try {
      // 断言:如果当前线程不是 UI 线程的话,就会抛出 Expected to run on UI thread! 的错误
      UiThreadUtil.assertOnUiThread();

      Assertions.assertCondition(
          mReactInstanceManager == null,
          "This root view has already been attached to a catalyst instance manager");
      
      // 赋值
      mReactInstanceManager = reactInstanceManager;
      mJSModuleName = moduleName;
      mAppProperties = initialProperties;
      mInitialUITemplate = initialUITemplate;
      
      // mUseSurface 是在构造函数中被赋值的   
      if (mUseSurface) {
        // TODO initialize surface here
      }
      
      /**
      * 在后台任务中触发 react context 的初始化(异步)
      * 此时会去预加载 js,并在 UI 的布局完成之前执行全局代码
      */    
      mReactInstanceManager.createReactContextInBackground();
      
      // 将 RootView 附加到 ReactInstanceManager 的成员变量 mAttachedReactRoots 中
      // 后文会再提到 mAttachedReactRoots
      attachToReactInstanceManager();

    } finally {
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    }
  }
  
  // ...
}

接下来我们看 ReactInstanceManager 对应代码的实现。

ReactInstanceManager

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java

  // ...
  public void createReactContextInBackground() {
    //略去日志输出代码
    
    // UI 线程断言
    UiThreadUtil.assertOnUiThread(); 
    
    // 是否已经初始化的判断
    if (!mHasStartedCreatingInitialContext) {
      mHasStartedCreatingInitialContext = true;
      recreateReactContextInBackgroundInner();
    }
  }
  
  /**
   * Recreate the react application and context. This should be called if configuration has changed
   * or the developer has requested the app to be reloaded. It should only be called after an
   * initial call to createReactContextInBackground.
   */
  @ThreadConfined(UI)
  public void recreateReactContextInBackground() {
    // ...
    
    recreateReactContextInBackgroundInner();
  }
  // ...

从代码中可以看到,createReactContextInBackground 在 Application 中只会被调用一次。当应用配置或者应用重新加载时,需要重新创建 ReactContext 信息,此时是去调用公开的 recreateReactContextInBackground 方法(注意:该方法有一个私有的实现,后续会提到)。但两个方法最终调用的实际都是 recreateReactContextInBackgroundInner 方法:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java

  // ...
  private void recreateReactContextInBackgroundInner() {
    // ...
    
    // 断言
    UiThreadUtil.assertOnUiThread();
    
    // 开发模式
    if (mUseDeveloperSupport && mJSMainModulePath != null) {
      final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();
        
      /**
      * 省略代码
      * 从上面条件的判断可以得知,这里主要是处理开发模式下,会从本地的服务器加载 bundle
      */

    }
    
    // 线上模式
    recreateReactContextInBackgroundFromBundleLoader();
  }
  
  @ThreadConfined(UI)
  private void recreateReactContextInBackgroundFromBundleLoader() {
    // 省略输出日志的代码
    // 调用私有实现
    recreateReactContextInBackground(mJavaScriptExecutorFactory, mBundleLoader);
  }
  
  // ...
  @ThreadConfined(UI)
  private void recreateReactContextInBackground(
      JavaScriptExecutorFactory jsExecutorFactory, JSBundleLoader jsBundleLoader) {
      
    // ...
    
    // 断言
    UiThreadUtil.assertOnUiThread();
    
    /** 
    * ReactContextInitParams 是一个私有类
    * 主要是初始化类的 mJsExecutorFactory 和 mJsBundleLoader
    * 后文会具体提到 ReactContextInitParams 类
    */
    final ReactContextInitParams initParams =
        new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
    if (mCreateReactContextThread == null) {
      // 开启新线程创建react context
      runCreateReactContextOnNewThread(initParams);
    } else {
      //创建 ReactContext 的后台任务已经开启,缓存initParams在队列中等待重新创建 ReactContext
      mPendingReactContextInitParams = initParams;
    }
  }
  
  // ...

recreateReactContextInBackground 方法的私有实现中,有两个形参:jsExecutorFactoryjsBundleLoader

  • jsExecutorFactory:js 执行器工厂实例,作为 C++ 和 js 双向通信的桥梁
  • jsBundleLoader:加载器实例,用于加载 js bundle

接着往下看 runCreateReactContextOnNewThread 的实现:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java

// ...

  private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
      // ...
      
      // 创建一个新线程
      mCreateReactContextThread = new Thread (
       null,
       new Runnable () {
          @Override
          public void run () {
             // ...
             
             try {
                // 设置当前线程的优先级
                Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
                // 创建 ReactContext 
                final ReactApplicationContext reactApplicationContext =
                      createReactContext(
                          initParams.getJsExecutorFactory().create(),
                          initParams.getJsBundleLoader());

                mCreateReactContextThread = null;
                // ...
                
                Runnable setupReactContextRunnable =
                      new Runnable() {
                        @Override
                        public void run() {
                          try {
                            // 后文会再提到这行代码
                            setupReactContext(reactApplicationContext);
                          } catch (Exception e) {
                            mDevSupportManager.handleException(e);
                          }
                        }
                      };
                
                // 开启线程运行 setupReactContextRunnable        
                reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable);
                UiThreadUtil.runOnUiThread(maybeRecreateReactContextRunnable);
             } catch (Exception e) {
                // ...
             }
          }
       }
      )
      
      // 启动线程
      mCreateReactContextThread.start();
  }

// ...

runCreateReactContextOnNewThread 方法的核心是调用 createReactContext 方法来创建 React Context:

// reac-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java

// ...
  private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
    
    // 略去日志输出代码
    
    /** 
    * 传入 Android Application 的实例去创建 ReactContext 实例
    * ReactApplicationContext 继承自 ReactContext,仅仅是简单包装了一下
    * Android Application,全部的功能都在 ReactContext 类中实现
    */
    final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
    
    // 设置 Native Module 调用的异常处理器
    NativeModuleCallExceptionHandler exceptionHandler =
        mNativeModuleCallExceptionHandler != null
            ? mNativeModuleCallExceptionHandler
            : mDevSupportManager;
    reactContext.setNativeModuleCallExceptionHandler(exceptionHandler);
    
    /** 
    * 创建 Native Module 注册表,将Java可调用的API暴露给 js
    * mPackages 在创建 ReactInstanceManager 实例时已被初始化
    */
    NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
    
    // 先处理 jsExecutor/nativeModuleRegistry/jsBundleLoader 等参数,便于后续构建 CatalystInstance 实例
    CatalystInstanceImpl.Builder catalystInstanceBuilder =
        new CatalystInstanceImpl.Builder()
            // 设置 React 队列配置(UI、js、native modules 三个线程队列)
            .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
            .setJSExecutor(jsExecutor)
            .setRegistry(nativeModuleRegistry)
            .setJSBundleLoader(jsBundleLoader)
            .setNativeModuleCallExceptionHandler(exceptionHandler);

    // 略去日志输出代码
    
    final CatalystInstance catalystInstance;
    try {
      // 创建 CatalystInstance 实例
      catalystInstance = catalystInstanceBuilder.build();
    } finally {
      // 略去日志输出代码
    }
    
    // 初始化 ReactContext 的队列配置(UI、js、native modules 三个线程队列)
    reactContext.initializeWithInstance(catalystInstance);
    
    /**
    * 初始化 JSI(JavaScript Interface)
    * JSI & JSC 的讨论:https://github.com/react-native-community/discussions-and-proposals/issues/91
    * 从上文可知,mJSIModulePackage 的值是 null
    */
    if (mJSIModulePackage != null) {
      catalystInstance.addJSIModules(
          mJSIModulePackage.getJSIModules(
              reactContext, catalystInstance.getJavaScriptContextHolder()));
      
      //TurboModules : https://github.com/react-native-community/discussions-and-proposals/issues/40
      if (ReactFeatureFlags.useTurboModules) {
        catalystInstance.setTurboModuleManager(
            catalystInstance.getJSIModule(JSIModuleType.TurboModuleManager));
      }
    }
    
    // 略去日志输出代码
    
    // 开始加载 js Bundle
    catalystInstance.runJSBundle();
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);

    return reactContext;
  }
// ...

createReactContext 方法主要作了如下四件事:

  • 创建 NativeModule 注册表,并交由 CatalystInstance 管理
  • 创建 CatalystInstance 实例
  • 如果存在 JSI Module,就对其进行注册
  • 关联 ReactContext 和 CatalystInstance,并开始加载 js bundle

CatalystInstance 是一个接口类型,定义了一系列 JSC(JavaScript Core) Bridge API 接口,提供了允许(从 Java 层)调用 JS 方法的环境,同时提供了部分 Java API 供 JS 层调用,其具体实现是 CatalystInstanceImpl 类。

下一篇我们继续接着分析 CatalystInstanceImpl 类。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK