zoukankan      html  css  js  c++  java
  • 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。
  • 相关阅读:
    golang匿名变量
    golang实现九九乘法表
    bootstrap输入框从数据库读取数据
    celery 使用multiprocessing 问题记录
    django 使用jquery ajax post数据问题
    centos 7 epel地址
    python 2.6升级到2.7
    python给多个发送邮件附件,参考于《python自动化运维》
    nginx+webpy 出现 upstream timed out
    webpy + nginx + fastcgi 构建python应用
  • 原文地址:https://www.cnblogs.com/lilei94/p/15151113.html
Copyright © 2011-2022 走看看