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. VUE 之_this.da 和 this

    作者:张艳涛 日期:2020-07-29 this用在第一层,如果在JS第3二层,要用 _this importfxx(obj) { let _this = this; let inputDOM = ...

  2. 我眼中的java线程池实现原理

    最近在看java线程池实现方面的源码,在此做个小结,因为网上关于线程池源码分析的博客挺多的,我也不打算重复造轮子啦,仅仅用纯语言描述的方式做做总结啦! 个人认为要想理解清楚java线程池实现原理,明白 ...

  3. 看视频学SignalR—在微软虚拟学院学习SignalR

    SignalR把实时Web功能变得异常简单. 如果您希望在几个小时内对SignalR有一个直观的了解,观看微软虚拟学院(MVA)的视频教学Lighting Up Real-Time Web Commu ...

  4. js 数据存入数组

    var tag = []; $('.deltag').each(function(){ if($(this).attr("checked")== 'checked'){ tag.p ...

  5. 【Java经验分享篇01】小白如何开始学会看开源项目?

    目录 前言 1.理解开源 1.1.什么是开源? 1.2.开源的定义 1.2.1.开源软件优点 1.2.2.经典开源软件案例 1.3.关于开源协议 1.3.1.如何选择开源协议 2.如何查找开源项目 2 ...

  6. 自动部署Springboot项目脚本小脚本

    #!/bin/bash echo '自动部署Springboot项目脚本...' # aaa.jar 项目jar包 pid=`ps -ef|grep aaa.jar|grep -v grep|grep ...

  7. SQL根据两个日期生成年、月、日

    1 DECLARE @beginTime DATETIME, @endTime DATETIME 2 SET @beginTime ='2019-03-01' 3 SET @endTime ='201 ...

  8. kubernetes/k8s CRI分析-kubelet创建pod分析

    先来简单回顾上一篇博客<kubernetes/k8s CRI 分析-容器运行时接口分析>的内容. 上篇博文先对 CRI 做了介绍,然后对 kubelet CRI 相关源码包括 kubele ...

  9. azure删除ns时一直处于terminating状态

    写个脚本 #!/bin/bash NAMESPACE=corekubectl proxy &kubectl get namespace $NAMESPACE -o json |jq '.spe ...

  10. rein 多平台支持的超便携端口转发与内网穿透工具

    介绍 本程序主要用于进行反向代理IP地址和端口,功能类似于 nginx 的 stream 模式和rinetd 的功能:在(1.0.5)版本开始,rein支持内网穿透,这一功能类似于frp 和ngrok ...