• React Native 启动流程简析


    导读:本文以 react-native-cli 创建的示例工程(安卓部分)为例,分析 React Native 的启动流程。

    工程创建步骤可以参考官网。本文所分析 React Native 版本为 v0.64.2

    我们知道上述工程是一个安卓应用,打开 android/ 目录下源码文件,首先发现它创建了两个 java 文件:MainApplication.javaMainActivity.java,分别做了应用以及主 Activity 的定义。

    安卓应用的启动流程是:在启动第一个 activity 之前会创建一个全局唯一的 Application 对象。故在此我们先分析 MainApplication

    MainApplication

    public class MainApplication extends Application implements ReactApplication {
      private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
        @Override
            public boolean getUseDeveloperSupport() {
              return BuildConfig.DEBUG;
            }
            @Override
            protected List<ReactPackage> getPackages() {
              @SuppressWarnings("UnnecessaryLocalVariable")
              List<ReactPackage> packages = new PackageList(this).getPackages();
              // 其它对 packages 的操作
              return packages;
            }
            @Override
            protected String getJSMainModuleName() {
              return "index";
            }
      }
      @Override
      public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
      }
      @Override
      public void onCreate() {
        super.onCreate();
        SoLoader.init(this, /* native exopackage */ false);
        initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
      }
    

    MainApplication 继承自 Application 类,并且实现了 ReactApplication 接口。在其中做的事情有:

    1. 创建成员变量 ReactNativeHost 的实例,并在创建过程中通过重写 ReactNativeHost 类方法的方式,注入一些配置,包括:
      1. getUseDeveloperSupport: 配置是否开启调试
      2. getPackages: 配置要加载的模块
      3. getJSMainModuleName: 配置 js 模块的入口文件名
    2. 在 onCreate 中:
      1. 调用 Soloader 库。Soloader 是 facebook 推出的一个 so 文件加载库,它能够处理 so 文件的依赖在 react-native 中,所有框架相关的 so 文件都是通过SoLoader完成加载的
      2. 通过 ReactInstanceManager 初始化 Flipper。Flipper 是 facebook 推出的用于 debug ios、Android、React Native 应用的工具。

    在这里简要介绍下 ReactNativeHostReactInstanceManager

    ReactNativeHost

    ReactNativeHost 是个抽象类,开发者可以重写其中的方法,其主要的作用是:在 application 中指定一些赋值操作,进而获取 ReactInstanceManager 的实例。所以可以把 ReactNativeHost 作为将用户自定义的参数赋值到 ReactInstanceManager 实例的中转站。核心方法是: getReactInstanceManager,详细分析见下文。

    ReactInstanceManager

    该类为核心类,主要负责管理 JS 的加载、维护生命周期、管理 JS 与 C++ 的交互等等。可以把 ReactInstanceManager 理解成 JS 与 C++ 的中转桥梁。

    MainActivity

    接着看 MainActivity.java

    public class MainActivity extends ReactActivity {
      @Override
      protected String getMainComponentName() {
        return "myProject";
      }
    }
    

    MainActivity 类中仅重写了 getMainComponentName 方法。该类继承自 ReactActivity,我们再来看其 ReactActivity

    public abstract class ReactActivity extends AppCompatActivity
        implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
      private final ReactActivityDelegate mDelegate;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mDelegate.onCreate(savedInstanceState);
      }
    

    ReactActivity 全权委托给 ReactActivityDelegate 来处理 onCreate 生命周期。来看 ReactActivityDelegateonCreate

    protected void onCreate(Bundle savedInstanceState) {
      String mainComponentName = getMainComponentName();
      mReactDelegate =
          new ReactDelegate(
              getPlainActivity(), getReactNativeHost(), mainComponentName, getLaunchOptions()) {
            @Override
            protected ReactRootView createRootView() {
              return ReactActivityDelegate.this.createRootView();
            }
          };
        if (mMainComponentName != null) {
          loadApp(mainComponentName);
        }
      }
    

    这里首先创建了 ReactDelegate 实例。接着来看 loadApp 方法:

    protected void loadApp(String appKey) {
      mReactDelegate.loadApp(appKey);
      getPlainActivity().setContentView(mReactDelegate.getReactRootView());
    }
    

    由此走到 ReactDelegate 实例的 loadApp 方法:

    public void loadApp(String appKey) {
      if (mReactRootView != null) {
        throw new IllegalStateException("Cannot loadApp while app is already running.");
      }
      mReactRootView = createRootView();
      mReactRootView.startReactApplication(
          getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
    }
    

    在这里做了三件事:创建 rootView (createRootView)、创建 ReactInstanceManager (getReactInstanceManager)、创建 ReactApplication (startReactApplication)。

    createRootView

    首先看下什么是 rootView。

    public class ReactRootView extends FrameLayout implements RootView, ReactRoot { /* ... */}
    

    ReactRootView 继承自 FrameLayout,并且实现了 RootViewReactRoot 两个接口。FrameLayout 是安卓几大布局中较为简单的一个,整个界面被当成一块空白备用区域,所有元素以左上角对齐堆叠。ReactRootView 继承自 FrameLayout,表明其也是作为简单布局而存在,UI 的绘制渲染都发生在上面。

    getReactInstanceManager

    ReactInstanceManager 是一个核心类,管理着 JS 的加载、C++ 和 JS 的交互、初始化参数等。最终调用来到 ReactNativeHost 类中的 createReactInstanceManager 方法:

    protected ReactInstanceManager createReactInstanceManager() {
      ReactInstanceManagerBuilder builder = /* ... */
    
      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();
      return reactInstanceManager;
    }
    

    此处做的事情如下:

    1. 创建 ReactInstanceManagerBuilder 实例。这里采用建造者模式来构造 ReactInstanceManager 实例,故在此先传入参数设定构造者;
    2. 把在 ReactNativeHost 中注册的 packages 都添加到 ReactInstanceManagerBuilder 实例中;
    3. 如果 getJSBundleFile 不为空,则加载对应的文件,否则加载默认的 jsBundleFile
    4. 调用 builder.build 方法。通过建造者真正构造 ReactInstanceManager 实例

    startReactApplication

      public void startReactApplication(/* */) {
        // ...
        try {
          // ...
          mReactInstanceManager.createReactContextInBackground();
        } finally {
          // ...
        }
      }
    

    最终执行到 ReactInstanceManagercreateReactContextInBackground 方法中。最后经过调用链:recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()

    runCreateReactContextOnNewThread 主要做了两件事:

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

    详细分析我们放到另一篇文章:React Native startReactApplication 流程梳理

    总结

    总结本文,通过 react-native-cli 创建的示例工程(安卓部分)为例,顺着两个类 MainApplicationMainActivity 的执行流程,抓住主干逻辑,最终梳理出了 React Native 从开始启动至执行用户 js 文件的过程。可以看到:

    MainApplication 的作用主要是传入用户的配置,并做 so 库以及应用 debug 工具的初始化工作;

    MainActivity 的作用主要是:

    1. 为应用创建 rootView 布局容器;
    2. 创建 ReactInstanceManager 核心类,用于后续管理 JS 的加载、C++ 和 JS 的交互、初始化参数等;
    3. 通过 startReactApplication 来创建 ReactContext 上下文,并最终调用到 AppRegistry.js 启动App。
  • 相关阅读:
    我的浏览器收藏夹分类
    我的浏览器收藏夹分类
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 318 最大单词长度乘积
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 316 去除重复字母
    Java实现 LeetCode 315 计算右侧小于当前元素的个数
    Java实现 LeetCode 315 计算右侧小于当前元素的个数
  • 原文地址:https://www.cnblogs.com/lilei94/p/15151113.html
Copyright © 2020-2023  润新知