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

首先来看 startReactApplication 的调用之处:

mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);

可以看到是在 rootView 上调用 startReactApplication,入参为 instanceManager、appKey、mLaunchOptions

顺着 startReactApplication 扒出其调用链:mReactInstanceManager.createReactContextInBackground() -> recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()

recreateReactContextInBackgroundReactInstanceManager 中的方法,做了两件事:

  1. 创建 ReactContextInitParams 实例 initParams,如下,其入参 jsExecutorFactory 为创建 ReactInstanceManager 时传入。

    final ReactContextInitParams initParams =
    new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
  2. 调用 runCreateReactContextOnNewThread

runCreateReactContextOnNewThreadReactInstanceManager 中的方法,主要做了两件事:

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

createReactContext

先看其调用的地方:

final ReactApplicationContext reactApplicationContext =
createReactContext(
initParams.getJsExecutorFactory().create(),
initParams.getJsBundleLoader());

其两个入参分别为 JsExecutorFactory 创建的 JavaScriptExecutor 实例,和 JsBundleLoader 实例。

JavaScriptExecutor

startReactApplication 第一个入参为 getReactNativeHost().getReactInstanceManager() 获取 ReactInstanceManager 实例。ReactInstanceManager 实例在 RN 应用中只有一个,先前在创建 MainActivity 时已创建。

回顾 React Native 启动流程简析,在创建过程中实际上是调用下面的方法:

ReactInstanceManager reactInstanceManager = builder.build()

builderReactInstanceManagerBuilder,我们来到该类的 build 方法,发现其最终是执行 return new ReactInstanceManager(...),在构造参数中第 4 个参数即为:getDefaultJSExecutorFactory,来到其定义处:

  private JavaScriptExecutorFactory getDefaultJSExecutorFactory(
String appName, String deviceName, Context applicationContext) {
try {
// If JSC is included, use it as normal
initializeSoLoaderIfNecessary(applicationContext);
SoLoader.loadLibrary("jscexecutor");
return new JSCExecutorFactory(appName, deviceName);
} catch (UnsatisfiedLinkError jscE) { /* ... */ }
}

也就是说在创建 ReactInstanceManagerBuilder 时我们就创建了 JSCExecutorFactory,并在随后调用其 create 方法创建 JSCExecutorJSCExecutorFactory 实现了 JavaScriptExecutorFactory 接口,其 create 方法如下,返回了 JSCExecutor 实例:

  @Override
public JavaScriptExecutor create() throws Exception {
WritableNativeMap jscConfig = new WritableNativeMap();
jscConfig.putString("OwnerIdentity", "ReactNative");
jscConfig.putString("AppIdentity", mAppName);
jscConfig.putString("DeviceIdentity", mDeviceName);
return new JSCExecutor(jscConfig);
}

再往下看 JSCExecutor 的定义,其继承自 JavaScriptExecutor 类:

@DoNotStrip
/* package */ class JSCExecutor extends JavaScriptExecutor {
static {
SoLoader.loadLibrary("jscexecutor");
}
/* package */ JSCExecutor(ReadableNativeMap jscConfig) {
super(initHybrid(jscConfig));
}
@Override
public String getName() {
return "JSCExecutor";
}
private static native HybridData initHybrid(ReadableNativeMap jscConfig);
}

于是就很清楚了,createReactContext 第一个参数为 JSCExecutor 实例,是通过 SoLoader 加载的 C++ 模块。

JsBundleLoader

同样的,在 return new ReactInstanceManager(...),其构造参数中第 5 个参数为:JSBundleLoader.createAssetLoader(mApplication, mJSBundleAssetUrl, false)

来到其定义之处,发现其返回了 JSBundleLoader 实例,并重写了其 loadScript 方法。

public static JSBundleLoader createAssetLoader(
final Context context, final String assetUrl, final boolean loadSynchronously) {
return new JSBundleLoader() {
@Override
public String loadScript(JSBundleLoaderDelegate delegate) {
delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
return assetUrl;
}
};
}

在创建完 JSCExecutor 实例和 JSBundleLoader 实例后,正式进入到 createReactContext 方法。

createReactContext

private ReactApplicationContext createReactContext(
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext); CatalystInstanceImpl.Builder catalystInstanceBuilder = /* ... */ try {
catalystInstance = catalystInstanceBuilder.build();
} finally { /* ... */ } reactContext.initializeWithInstance(catalystInstance); TurboModuleManager turboModuleManager =
new TurboModuleManager( /* ... */ ) catalystInstance.setTurboModuleManager(turboModuleManager); if (mJSIModulePackage != null) {
catalystInstance.addJSIModules( /* ... */ );
} catalystInstance.runJSBundle();
return reactContext;

在这里面,首先创建了 reactContext,并通过 catalystInstanceBuilder 创建了 catalystInstance。接着通过 initializeWithInstance 方法将 reactContextcatalystInstance 关联起来,并进行了一系列的为 catalystInstance 初始化的工作。最后进入到方法 catalystInstance.runJSBundle() 中。

initializeWithInstance

通过调用 getUIQueueThreadgetNativeModulesQueueThreadgetJSQueueThread创建了3个线程队列,分别是 UI线程、NativeModules 线程,和 JS 线程。

runJSBundle

public void runJSBundle() {
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
synchronized (mJSCallsPendingInitLock) {
mAcceptCalls = true;
for (PendingJSCall function : mJSCallsPendingInit) {
function.call(this);
}
mJSCallsPendingInit.clear();
mJSBundleHasLoaded = true;
}
Systrace.registerListener(mTraceListener);
}

通过先前返回的 mJSBundleLoader 执行其 loadScript 方法:

public String loadScript(JSBundleLoaderDelegate delegate) {
delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
return assetUrl;
}

loadScriptFromAssets 方法在 CatalystInstanceImpl 中:

public void loadScriptFromAssets(
AssetManager assetManager, String assetURL, boolean loadSynchronously) {
mSourceURL = assetURL;
jniLoadScriptFromAssets(assetManager, assetURL, loadSynchronously);
}

这里的 assetURL 是在 createAssetLoader 创建 mJSBundleLoader 时传入,其赋值时机是在 reactInstanceManagerBuilder 实例中,由 reactNativeHost 实例的 createReactInstanceManager 方法。若 开发者在 MainApplication.java 中通过重写 getJSBundleFile 方法自定义了 assetURL 则使用该 url,否则使用系统默认,如:file://sdcard/myapp_cache/index.android.bundle

jniLoadScriptFromAssets 方法为 C++ 侧定义的方法,用于读取 js 文件。为什么 Java 代码中可以直接调用 C++ 方法,这里还要打个问号,后续在分析 Java 与 C++ 通信及 Java 与 JS 通信时阐释。

通过 createReactContext 创建了 reactContext,创建了 catalystInstance 实例,并将上述两者关联,接着通过 catalystInstance 读入 js 文件。接下来就进入到 setupReactContext 的环节。

setupReactContext

private void setupReactContext(final ReactApplicationContext reactContext) {
synchronized (mAttachedReactRoots) {
catalystInstance.initialize();
for (ReactRoot reactRoot : mAttachedReactRoots) {
if (reactRoot.getState().compareAndSet(ReactRoot.STATE_STOPPED, ReactRoot.STATE_STARTED)) {
attachRootViewToInstance(reactRoot);
}
}
}
UiThreadUtil.runOnUiThread(
public void run() {
listener.onReactContextInitialized(reactContext);
}
)
reactContext.runOnJSQueueThread(
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
)
reactContext.runOnNativeModulesQueueThread(
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
)
}

这里面做的事情如下:

  1. catalystInstance.initialize(): 所有原生模块的初始化
  2. attachRootViewToInstance(reactRoot): 绘制所有的 RootView 并添加到相应实例并设置相应的监听事件
  3. 创建 UI 模块、JS 模块和原生模块线程,并设置 JS 模块和原生模块所在线程的优先级

总结本文

从 createReactContext 和 setupReactContext 两个方法的源码出发,分析了 RN startReactApplication 方法的执行过程,其中:

createReactContext 的主要作用是:创建 reactContext、创建 catalystInstance 实例,并将上述两者关联,接着通过 catalystInstance 读入 js 文件。

setupReactContext的主要作用是:初始化所有原生模块,绘制所有 rootview,创建 UI 模块、JS 模块和原生模块线程,并设置优先级。

React Native startReactApplication 方法简析的更多相关文章

  1. React Native 启动流程简析

    导读:本文以 react-native-cli 创建的示例工程(安卓部分)为例,分析 React Native 的启动流程. 工程创建步骤可以参考官网.本文所分析 React Native 版本为 v ...

  2. Linux网络性能优化方法简析

    Linux网络性能优化方法简析 2010-12-20 10:56 赵军 IBMDW 字号:T | T 性能问题永远是永恒的主题之一,而Linux在网络性能方面的优势则显而易见,这篇文章是对于Linux ...

  3. React Native升级方法——升级到最新版本0.59

    React Native最近有大动作,于2019年3月12日发布新版本0.59.主要有两点值得升级:支持React Hooks:升级了JavaScriptCore,使Android性能有大幅提升.据用 ...

  4. Linux 下网络性能优化方法简析

    概述 对于网络的行为,可以简单划分为 3 条路径:1) 发送路径,2) 转发路径,3) 接收路径,而网络性能的优化则可基于这 3 条路径来考虑.由于数据包的转发一般是具备路由功能的设备所关注,在本文中 ...

  5. 正则表达式中Pattern类、Matcher类和matches()方法简析

    1.简介:  java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.  它包括两个类:Pattern和Matcher . Pattern: 一个Pattern是一 ...

  6. hashcode方法 简析

    package com.ycgwl; import java.util.HashMap; class People{ private String name; private int age; pub ...

  7. React Native for Android 学习

    前言 Facebook 在2015.9.15发布了 React Native for Android,把 JavaScript 开发技术扩展到了移动Android平台.基于React的React Na ...

  8. React Native随笔——警告处理方法(持续更新)

    一.警告propTypes was defined as an instance property on commonTabar. Use a static property to define pr ...

  9. 简析 __init__、__new__、__call__ 方法

    简析 __init__.__new__.__call__ 方法 任何事物都有一个从创建,被使用,再到消亡的过程,在程序语言面向对象编程模型中,对象也有相似的命运:创建.初始化.使 用.垃圾回收,不同的 ...

随机推荐

  1. ts 学习笔记 - 类

    目录 类 类的概念 类的用法 属性和方法 类的继承 存取器 静态属性 Typescript 中的用法 抽象类 类的类型 类与接口 类实现接口 接口继承接口 接口继承类 混合类型 类 类的概念 类 (c ...

  2. Linux从头学05-系统启动过程中的几个神秘地址,你知道是什么意思吗?

    作 者:道哥,10+年的嵌入式开发老兵. 公众号:[IOT物联网小镇],专注于:C/C++.Linux操作系统.应用程序设计.物联网.单片机和嵌入式开发等领域. 公众号回复[书籍],获取 Linux. ...

  3. time() 在thinkphp 3.2.3 模板格式化输出

    {$ltime|date="Y-m-d",###}

  4. 某学院m3u8视频解密获取分析实战分享

    [免责声明]本文来源于作者个人学习整理,仅供学习交流使用,不构成商业目的.所有资源均系本人个人学习或网络收集,仅提供一个展示.介绍.观摩学习的博文,不对其内容的准确性.可靠性.正当性.安全性.合法性等 ...

  5. Java异常02——自定义异常

    Error与Exception与自定义异常 Error与Exception Error Exception 自定义异常 快捷键: ctrl + alt + t  package exception.d ...

  6. HCIA—(网络初相识)

    网络 IT--> 信息服务 -->数据底层 云计算 大数据 CT--> 通信服务 -->通信服务 构建数据通信的协议 ICT-->信息通信 数据 + 通信 数通 基础 R ...

  7. Spring源码阅读-BeanFactory体系结构分析

    BeanFactory是Spring中非常重要的一个类,搞懂了它,你就知道了bean的初始化和摧毁过程,对于深入理解IOC有很大的帮助. BeanFactory体系结构 首先看一下使用IDEA生成的继 ...

  8. JavaScript new 关键词解析及原生实现 new

    java里面,new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,原型语言没类,只有对象与原型链继承 JavaScript 中 new 表达式的作用是生成 ...

  9. TotalCommander的两款目录插件

    CatalogMaker 与 DiskDir Extended 是两个用于生成文件夹目录的totalCmd插件. 将指定目录下所有文件.文件夹以指定格式存储在一个文本文件中,可作为EverCD+的轻量 ...

  10. Linux服务器下JVM堆栈信息dump及问题排查

    #dump 方法栈信息 jstack $pid > /home/$pid/jstack.txt #dump jvm内存使用情况 jmap -heap $pid > /home/$pid/j ...