导读:本文以 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。

React Native 启动流程简析的更多相关文章

  1. React Native startReactApplication 方法简析

    在 React Native 启动流程简析 这篇文章里,我们梳理了 RN 的启动流程,最后的 startReactApplication 由于相对复杂且涉及到最终执行前端 js 的流程,我们单独将其提 ...

  2. Tomcat启动流程简析

    Tomcat是一款我们平时开发过程中最常用到的Servlet容器.本系列博客会记录Tomcat的整体架构.主要组件.IO线程模型.请求在Tomcat内部的流转过程以及一些Tomcat调优的相关知识. ...

  3. Android 启动过程简析

    首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...

  4. zxing二维码扫描的流程简析(Android版)

    目前市面上二维码的扫描似乎用开源google的zxing比较多,接下去以2.2版本做一个简析吧,勿喷... 下载下来后定位两个文件夹,core和android,core是一些核心的库,android是 ...

  5. OpenStack Cinder源代码流程简析

    版权声明:本博客欢迎转载,转载时请以超链接形式标明文章原始出处!谢谢! 博客地址:http://blog.csdn.net/i_chips 一.概况 OpenStack的各个模块都有对应的client ...

  6. LinkedHashMap结构get和put源码流程简析及LRU应用

    原理这篇讲得比较透彻Java集合之LinkedHashMap. 本文属于源码阅读笔记,因put,get调用逻辑及链表维护逻辑复杂(至少网上其它文章的逻辑描述及配图,我都没看明白LinkedHashMa ...

  7. jquery选择器的实现流程简析及提高性能建议!

    当我们洋洋得意的使用jquery强大的选择器功能时有没有在意过jquery的选择性能问题呢,其实要想高效的使用jquery选择器,了解其实现流程是很有必要的,那么这篇文章我就简单的讲讲其实现流程,相信 ...

  8. 【Java虚拟机10】ClassLoader.getSystemClassLoader()流程简析

    前言 学习类加载必然离开不了sun.misc.Launcher这个类和Class.forName()这个方法. 分析ClassLoader.getSystemClassLoader()这个流程可以明白 ...

  9. HTTPS及流程简析

    [序] 在我们在浏览某些网站的时候,有时候浏览器提示需要安装根证书,可是为什么浏览器会提示呢?估计一部分人想也没想就直接安装了,不求甚解不好吗? 那么什么是根证书呢?在大概的囫囵吞枣式的百度之后知道了 ...

随机推荐

  1. SPF Tarjan算法求无向图割点(关节点)入门题

    SPF 题目抽象,给出一个连通图的一些边,求关节点.以及每个关节点分出的连通分量的个数 邻接矩阵只要16ms,而邻接表却要32ms,  花费了大量的时间在加边上. //   time  16ms 1 ...

  2. 『无为则无心』Python序列 — 24、Python序列的推导式

    目录 1.列表推导式 (1)快速体验 (2)带if的列表推导式 (3)多个for循环实现列表推导式 2.字典推导式 (1)创建一个字典 (2)将两个列表合并为一个字典 (3)提取字典中目标数据 3.集 ...

  3. Java Collecion的常用方法

    import java.util.*; /** * 数组存储数据的特点: * ①一旦初始化,大小确定 * ②一旦定义,存储的类型确定且相同. * 数组存储的弊端: * ①大小无法改变. * ②方法较少 ...

  4. Linux:获取Linux离线postgresql数据库安装包并部署

    获取离线安装包(方法一) 1. 进入官网:https://www.postgresql.org/ 2. 下载步骤 (1)点击Download (2) 选择安装的服务器的版本  (3)更新yum源 #更 ...

  5. Http协议知识点回顾

    OSI七层协议: 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 完整的Http协议请求流程: 协议:客户端和服务器双方都能听的懂得语言(约定) 不需要关注底层的网络层或传输层协议 TCP ...

  6. Python + Requests 知识点回顾

    http://www.downza.cn/soft/11145.html PS下载地址 http://www.bejson.com/jsonviewernew/ JSON的在线视图 import re ...

  7. 使用Pycharm创建Django项目无法创建app.

    Python3.7使用Django1.11.7创建Django项目报以下错误时: 在使用Pycharm创建Django项目报以下错误时: Traceback (most recent call las ...

  8. odoo里的rpc用法

    import odoorpcdb_name = 'test-12'user_name = 'admin'password = 'admin'# Prepare the connection to th ...

  9. odoo12动作里添加向导

    在odoo12的 动作里添加向导例子1:只会在tree视图里显示,不会在form里显示 <act_window id="action_change_stage_ttest" ...

  10. python中的常用内建属性与内建函数

    常用专有属性常用专有属性 说明 触发方式 __init__ 构造初始化函数 创建实例后,赋值时使用,在__new__后 __new__ 生成实例所需属性 创建实例时 __class__ 实例所在的类 ...