React Native 启动流程简析
导读:本文以 react-native-cli
创建的示例工程(安卓部分)为例,分析 React Native 的启动流程。
工程创建步骤可以参考官网。本文所分析 React Native
版本为 v0.64.2
。
我们知道上述工程是一个安卓应用,打开 android/
目录下源码文件,首先发现它创建了两个 java 文件:MainApplication.java
和 MainActivity.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
接口。在其中做的事情有:
- 创建成员变量
ReactNativeHost
的实例,并在创建过程中通过重写 ReactNativeHost 类方法的方式,注入一些配置,包括:- getUseDeveloperSupport: 配置是否开启调试
- getPackages: 配置要加载的模块
- getJSMainModuleName: 配置 js 模块的入口文件名
- 在 onCreate 中:
- 调用 Soloader 库。
Soloader
是 facebook 推出的一个 so 文件加载库,它能够处理 so 文件的依赖在 react-native 中,所有框架相关的 so 文件都是通过SoLoader完成加载的 - 通过
ReactInstanceManager
初始化 Flipper。Flipper
是 facebook 推出的用于 debug ios、Android、React Native 应用的工具。
- 调用 Soloader 库。
在这里简要介绍下 ReactNativeHost
和 ReactInstanceManager
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
生命周期。来看 ReactActivityDelegate
的 onCreate
。
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
,并且实现了 RootView
、ReactRoot
两个接口。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;
}
此处做的事情如下:
- 创建
ReactInstanceManagerBuilder
实例。这里采用建造者模式来构造ReactInstanceManager
实例,故在此先传入参数设定构造者; - 把在
ReactNativeHost
中注册的packages
都添加到ReactInstanceManagerBuilder
实例中; - 如果
getJSBundleFile
不为空,则加载对应的文件,否则加载默认的jsBundleFile
; - 调用
builder.build
方法。通过建造者真正构造ReactInstanceManager
实例
startReactApplication
public void startReactApplication(/* */) {
// ...
try {
// ...
mReactInstanceManager.createReactContextInBackground();
} finally {
// ...
}
}
最终执行到 ReactInstanceManager
的 createReactContextInBackground
方法中。最后经过调用链:recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()
runCreateReactContextOnNewThread
主要做了两件事:
- 创建一个新的线程,并在新线程中通过
createReactContext
创建ReactContext
上下文; - 通过
setupReactContext
来设置上下文环境,并最终调用到AppRegistry.js
启动App。
详细分析我们放到另一篇文章:React Native startReactApplication 流程梳理。
总结
总结本文,通过 react-native-cli
创建的示例工程(安卓部分)为例,顺着两个类 MainApplication
和 MainActivity
的执行流程,抓住主干逻辑,最终梳理出了 React Native
从开始启动至执行用户 js
文件的过程。可以看到:
MainApplication
的作用主要是传入用户的配置,并做 so
库以及应用 debug
工具的初始化工作;
MainActivity
的作用主要是:
- 为应用创建
rootView
布局容器; - 创建
ReactInstanceManager
核心类,用于后续管理 JS 的加载、C++ 和 JS 的交互、初始化参数等; - 通过
startReactApplication
来创建ReactContext
上下文,并最终调用到AppRegistry.js
启动App。
React Native 启动流程简析的更多相关文章
- React Native startReactApplication 方法简析
在 React Native 启动流程简析 这篇文章里,我们梳理了 RN 的启动流程,最后的 startReactApplication 由于相对复杂且涉及到最终执行前端 js 的流程,我们单独将其提 ...
- Tomcat启动流程简析
Tomcat是一款我们平时开发过程中最常用到的Servlet容器.本系列博客会记录Tomcat的整体架构.主要组件.IO线程模型.请求在Tomcat内部的流转过程以及一些Tomcat调优的相关知识. ...
- Android 启动过程简析
首先我们先来看android构架图: android系统是构建在linux系统上面的. 所以android设备启动经历3个过程. Boot Loader,Linux Kernel & Andr ...
- zxing二维码扫描的流程简析(Android版)
目前市面上二维码的扫描似乎用开源google的zxing比较多,接下去以2.2版本做一个简析吧,勿喷... 下载下来后定位两个文件夹,core和android,core是一些核心的库,android是 ...
- OpenStack Cinder源代码流程简析
版权声明:本博客欢迎转载,转载时请以超链接形式标明文章原始出处!谢谢! 博客地址:http://blog.csdn.net/i_chips 一.概况 OpenStack的各个模块都有对应的client ...
- LinkedHashMap结构get和put源码流程简析及LRU应用
原理这篇讲得比较透彻Java集合之LinkedHashMap. 本文属于源码阅读笔记,因put,get调用逻辑及链表维护逻辑复杂(至少网上其它文章的逻辑描述及配图,我都没看明白LinkedHashMa ...
- jquery选择器的实现流程简析及提高性能建议!
当我们洋洋得意的使用jquery强大的选择器功能时有没有在意过jquery的选择性能问题呢,其实要想高效的使用jquery选择器,了解其实现流程是很有必要的,那么这篇文章我就简单的讲讲其实现流程,相信 ...
- 【Java虚拟机10】ClassLoader.getSystemClassLoader()流程简析
前言 学习类加载必然离开不了sun.misc.Launcher这个类和Class.forName()这个方法. 分析ClassLoader.getSystemClassLoader()这个流程可以明白 ...
- HTTPS及流程简析
[序] 在我们在浏览某些网站的时候,有时候浏览器提示需要安装根证书,可是为什么浏览器会提示呢?估计一部分人想也没想就直接安装了,不求甚解不好吗? 那么什么是根证书呢?在大概的囫囵吞枣式的百度之后知道了 ...
随机推荐
- frp+nginx内网穿透
frp+nginx内网穿透 背景:自己有台内网Linux主机,希望被外网访问(ssh.http.https): 准备工作 内网Linux主机-c,可以访问c主机和外网的主机-s(windows/lin ...
- 1、springcloud gateway
参考: https://www.cnblogs.com/babycomeon/p/11161073.html 1.springcloud gateway1.1.依赖-初体验https://www.cn ...
- php自动识别背景并且把它改为白色
此源码有个阈值可以自己调节,精确度等自测 <?php /*$Colorimg = new Colorimg(); $image=$Colorimg->IMGaction("G:/ ...
- 永恒之蓝ms17_010漏洞复现
1.什么是永恒之蓝 永恒之蓝(Eternal Blue)爆发于2017年4月14日晚,是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限,以此来控制被入侵的计算机. 2.SMB协议 SM ...
- C语言:编程求任意月份的天数
闰年问题,因为二月份的天数与闰年有关.闰年的判断依据是:若某年能被4整除,但不能被100整除,则这一年是闰年:若某年能被400整除,则这一年也是闰年 #include <stdio.h> ...
- 给potplayer配置iptv源,看所有你想看的电视
目录 一.展示: 二.下载 三.播放 一.展示: 二.下载 Github 上的开源项目:iptv-org/iptv 传送门: https://github.com/iptv-org/iptv 该项目包 ...
- 整理最近用的Mongo查询语句
背景 最近做了几个规则逻辑.用到mongo查询比较多,就是查询交易信息跑既定规则筛选出交易商户,使用聚合管道进行统计和取出简单处理后的数据,用SQL代替业务代码逻辑的判断. 方法 MongoDB聚合使 ...
- YsoSerial 工具常用Payload分析之CC1
前文介绍了最简单的反序列化链URLDNS,虽然URLDNS本身不依赖第三方包且调用简单,但不能做到漏洞利用,仅能做漏洞探测,如何才能实现RCE呢,于是就有Common-collections1-7.C ...
- 将已经基本完成的项目推送到gitee上管理
1,首先在gitee上创建仓库,保留一个.gitigonore就行,这样就得到一个远程仓库地址 2,仓库有文件,那么就需要先将这个文件pull下来: 在本地的项目目录中执行:git init git ...
- C语言变量为何先定义后使用
C语言中,对变量的使用,首先要先定义.说明其数据类型.原因可能如下: 1不同类型的变量,其编码表示方式可能不同. 2不同类型的变量,其占有的空间大小不同.不事先说明无法在内存中开辟空间.