接触RN开发也快两年的时间了,期间也开发了5、6个APP了,ReactNative的版本也在快速的迭代着,今天重新出发,从源码解析一下App的启动流程,此次解析基于RN 0.60.5版本。
开始之前
开始分析之前,新建一个名为RnDemo的空项目,RN版本选择0.60.5,通过查看项目的目录结构中Android部分会自动为我们生成MainActivity.java和MainApplication.java文件,我们的分析就从这两个文件入手。
Java部分,开始上传
1.首先看一下MainApplication文件,继承Application并实现了ReactApplication接口,主要做一写RN的初始化操作。
1 | public class MainApplication extends Application implements ReactApplication { |
2.接下来看一下MainActivity文件,继承自ReactActivity,ReactActivity作为JS页面的真正容器
1 | public class MainActivity extends ReactActivity { |
3.继续,来看一下ReactActivity来,
1 | public abstract class ReactActivity extends AppCompatActivity |
从以上代码可以看到,真正实现是在ReactActivityDelegate类中进行的。
4.继续,我们重点看一下ReactActivityDelegate中的内容
1 | public class ReactActivityDelegate { |
看看ReactActivityDelegate做了那些工作:
1 | 1.创建ReactRootView作为根视图 |
由此看来ReactRootView是个关键,进入ReactRootView类继续看一下启动RN的startReactApplication方法,它接受三个参数:ReactInstanceManager,appName,启动的设置参数launchOptions,
1 | /** |
接下来,进入ReactInstanceManger类看一下createReactContextInBackground方法,
1 | /** |
createReactContextInBackground方法仅会在首次启动时调用,重新加载(reloaded)app时,会调用recreateReactContextInBackground(),两个方法都会调用recreateReactContextInBackgroundInner(),
1 | @ThreadConfined(UI) |
recreateReactContextInBackgroundFromBundleLoader方法向下调用recreateReactContextInBackground方法
1 | @ThreadConfined(UI) |
在runCreateReactContextOnNewThread方法中,我们看到是ReactInstanceManager.createReactContext方法最终创建了ReactApplicationContext,我们继续看createReactContext()方法,有关此方法的2个参数:
JSCJavaScriptExecutor jsExecutor:JSCJavaScriptExecutor继承于JavaScriptExecutor,当该类被加载时,它会自动去加载”reactnativejnifb.so”库,并会调用Native方
法initHybrid()初始化C++层RN与JSC通信的框架。
JSBundleLoader jsBundleLoader:缓存了JSBundle的信息,封装了上层加载JSBundle的相关接口,CatalystInstance通过其简介调用ReactBridge去加载JS文件,不同的场景会创建
不同的加载器,具体可以查看类JSBundleLoader。
1 | private ReactApplicationContext createReactContext( |
createReactContext方法中用catalystInstance.runJSBundle() 来加载 JS bundle
1 | @Override |
查看loadScript方法,参数JSBundleLoaderDelegate接口的实现类CatalystInstanceImpl,我们假设调用了loadScriptFromAssets方法,
1 | @Override |
CatalystInstanceImpl.java最终还是调用C++层的CatalystInstanceImpl.cpp去加载JS Bundle。
接下来看一下CatalystInstance的实现类CatalystInstanceImpl的构造方法:
1 | private CatalystInstanceImpl( |
参数解读:
- ReactCallback:CatalystInstanceImpl的静态内部类ReactCallback,负责接口回调
- JavaScriptExecutor: js执行器,将js的调用传给c++层
- MessageQueueThread jsQueue:js线程
- MessageQueueThread moduleQueue: java线程
- javaModules: java module
- cxxModules: c++ module
好累,😀,继续,我们去c++层看一下,在项目的node_modules/react-native/ReactAndroid/src/main/jni/react/jni可以找到CatalystInstanceImpl.cpp
看一下CatalystInstanceImpl::jniLoadScriptFromAssets1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29void CatalystInstanceImpl::jniLoadScriptFromAssets(
jni::alias_ref<JAssetManager::javaobject> assetManager,
const std::string& assetURL,
bool loadSynchronously) {
const int kAssetsLength = 9; // strlen("assets://");
//获取source js Bundle的路径名,这里默认的就是index.android.bundle
auto sourceURL = assetURL.substr(kAssetsLength);
//assetManager是Java层传递过来的AssetManager,调用JSLoade.cpo里的extractAssetManager()方法,extractAssetManager()再
//调用android/asset_manager_jni.h里的AssetManager_fromJava()方法获取AssetManager对象。
auto manager = extractAssetManager(assetManager);
// 调用JSloader.cpp的loadScriptFromAssets方法,读取js Bundle里面的内容
auto script = loadScriptFromAssets(manager, sourceURL);
// unbundle命令打包判断,build.gradle默认里是bundle打包方式。
if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
auto bundle = JniJSModulesUnbundle::fromEntryFile(manager, sourceURL);
auto registry = RAMBundleRegistry::singleBundleRegistry(std::move(bundle));
instance_->loadRAMBundle(
std::move(registry),
std::move(script),
sourceURL,
loadSynchronously);
return;
} else if (Instance::isIndexedRAMBundle(&script)) {
instance_->loadRAMBundleFromString(std::move(script), sourceURL);
} else {
//bundle命令打包走此流程,instance_是Instan.h中类的实例
instance_->loadScriptFromString(std::move(script), sourceURL, loadSynchronously);
}
}
在项目node_modules/react-native/ReactCommon的cxxReact的NativeToJsBridge.cpp文件:
1 | void NativeToJsBridge::loadApplication( |
进入项目node_modules/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp进一步调用JSIExecutor.cpp的loadApplicationScript()方法。
1 | //解释执行JS |
flushedQueueJS支线的是MessageQueue.js的flushedQueue()方法,此时JS已经被加载到队列中,等待Java层来驱动它。加载完JS后,返回reactApplicationContext,我们继续跟进它的实现。
我们回到ReactInstanceManager类的runCreateReactContextOnNewThread方法中,看到setupReactContext()方法,进入之后可以看到attachRootViewToInstance(reactRoot)方法,进入后
1 | private void attachRootViewToInstance(final ReactRoot reactRoot) { |
catalystInstance.getJSModule(AppRegistry.class)AppRegistry.class是JS层暴露给Java层的接口方法,它的真正实现在AppRegistry.js里,AppRegistry.js是运行所有RN应用的JS层入口,
1 | runApplication(appKey: string, appParameters: any): void { |
😀,基本到这,就会去调用JS进行组件渲染,再通过Java层的UIManagerModule将JS组件转换为Android组件,最终显示在ReactRootView上,即完成启动过程。😀
阅读源代码还是挺耗时的事情,哈哈。
同步更新至个人公众号及博客。
CSDN:https://blog.csdn.net/wayne214
公众号:君伟说。