共计 19411 个字符,预计需要花费 49 分钟才能阅读完成。
接触 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 的初始化操作。
public class MainApplication extends Application implements ReactApplication { | |
// 实现 ReactApplication 接口,创建 ReactNativeHost 成员变量,持有 ReactInstanceManager 实例,做一些初始化操作。private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {// 是否开启 dev 调试, 及一些调试工具,比如 redbox(红盒),有时我们看到的报错 | |
@Override | |
public boolean getUseDeveloperSupport() {return BuildConfig.DEBUG;} | |
// 返回 app 需要的 ReactPackage,添加需要加载的模块,这个地方就是我们在项目中添加依赖包时需要添加第三方 package 的地方 | |
@Override | |
protected List<ReactPackage> getPackages() {@SuppressWarnings("UnnecessaryLocalVariable") | |
List<ReactPackage> packages = new PackageList(this).getPackages(); | |
// Packages that cannot be autolinked yet can be added manually here, for example: | |
// packages.add(new MyReactNativePackage()); | |
return packages; | |
} | |
@Override | |
protected String getJSMainModuleName() {return "index";} | |
}; | |
@Override | |
public ReactNativeHost getReactNativeHost() {return mReactNativeHost;} | |
@Override | |
public void onCreate() {super.onCreate(); | |
//SoLoader:加载 C ++ 底层库,准备解析 JS。SoLoader.init(this, /* native exopackage */ false); | |
} | |
} |
2. 接下来看一下 MainActivity 文件, 继承自 ReactActivity,ReactActivity 作为 JS 页面的真正容器
public class MainActivity extends ReactActivity { | |
/** | |
* Returns the name of the main component registered from JavaScript. | |
* This is used to schedule rendering of the component. | |
*/ | |
@Override | |
protected String getMainComponentName() { | |
// 返回组件名,和 js 入口注册名字一致 | |
return "RnDemo"; | |
} | |
} | |
对应的 js 模块注册名字中:AppRegistry.registerComponent("RnDemo", () => App); |
3. 继续,来看一下 ReactActivity 来,
public abstract class ReactActivity extends AppCompatActivity | |
implements DefaultHardwareBackBtnHandler, PermissionAwareActivity { | |
private final ReactActivityDelegate mDelegate; | |
protected ReactActivity() {mDelegate = createReactActivityDelegate(); | |
} | |
/** | |
* Called at construction time, override if you have a custom delegate implementation. | |
*/ | |
protected ReactActivityDelegate createReactActivityDelegate() {return new ReactActivityDelegate(this, getMainComponentName()); | |
} | |
... | |
@Override | |
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState); | |
mDelegate.onCreate(savedInstanceState); | |
} | |
protected final ReactNativeHost getReactNativeHost() {return mDelegate.getReactNativeHost(); | |
} | |
protected final ReactInstanceManager getReactInstanceManager() {return mDelegate.getReactInstanceManager(); | |
} | |
protected final void loadApp(String appKey) {mDelegate.loadApp(appKey); | |
} | |
} |
从以上代码可以看到,真正实现是在 ReactActivityDelegate 类中进行的。
4. 继续,我们重点看一下 ReactActivityDelegate 中的内容
public class ReactActivityDelegate {protected void onCreate(Bundle savedInstanceState) {//mMainComponentName 就是上面 ReactActivity.getMainComponentName()返回的组件名 | |
String mainComponentName = getMainComponentName(); | |
if (mainComponentName != null) { | |
// 加载 app 页面 | |
loadApp(mainComponentName); | |
} | |
// 双击判断工具类 | |
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();} | |
protected void loadApp(String appKey) { | |
// 非空判断 | |
if (mReactRootView != null) {throw new IllegalStateException("Cannot loadApp while app is already running."); | |
} | |
// 创建 ReactRootView 作为根视图, 它本质上是一个 FrameLayout | |
mReactRootView = createRootView(); | |
// 启动 RN 应用,并完成一些初始化设置 | |
mReactRootView.startReactApplication(getReactNativeHost().getReactInstanceManager(), | |
appKey, | |
getLaunchOptions()); | |
// 将 ReactRootView 作为 Activity 的显示 view | |
getPlainActivity().setContentView(mReactRootView); | |
} |
看看 ReactActivityDelegate 做了那些工作:
1. 创建 ReactRootView 作为根视图 | |
2.startReactApplication 启动 RN 流程 | |
3. 将 ReactRootView 作为 ReactActivity 的内容显示 view |
由此看来 ReactRootView 是个关键,进入 ReactRootView 类继续看一下启动 RN 的 startReactApplication 方法,它接受三个参数:ReactInstanceManager,appName, 启动的设置参数 launchOptions,
/** | |
* Schedule rendering of the react component rendered by the JS application from the given JS | |
* module (@{param moduleName}) using provided {@param reactInstanceManager} to attach to the | |
* JS context of that manager. Extra parameter {@param launchOptions} can be used to pass initial | |
* properties for the react component. | |
*/ | |
public void startReactApplication( | |
ReactInstanceManager reactInstanceManager, | |
String moduleName, | |
@Nullable Bundle initialProperties, | |
@Nullable String initialUITemplate) {Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "startReactApplication"); | |
try {UiThreadUtil.assertOnUiThread(); | |
// TODO(6788889): Use POJO instead of bundle here, apparently we can't just use WritableMap | |
// here as it may be deallocated in native after passing via JNI bridge, but we want to reuse | |
// it in the case of re-creating the catalyst instance | |
Assertions.assertCondition( | |
mReactInstanceManager == null, | |
"This root view has already been attached to a catalyst instance manager"); | |
// reactInstanceManage 实例, 管理 React 实例 | |
mReactInstanceManager = reactInstanceManager; | |
// js 注册的 name, 同 ReactActivity.getMainComponentName()与 AppRegistry.registerComponent()放回一致 | |
mJSModuleName = moduleName; | |
// 是 Native 向 JS 传递的数据,以后可能由 POJO 代替,默认是 null,需要的话要重写 createReactActivityDelegate,并重写其中 getLaunchOptions 方法 | |
mAppProperties = initialProperties; | |
mInitialUITemplate = initialUITemplate; | |
if (mUseSurface) {// TODO initialize surface here} | |
// 创建 RN 的上下文 ReactContext | |
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {mReactInstanceManager.createReactContextInBackground(); | |
} | |
// 宽高计算完成后添加布局监听 | |
attachToReactInstanceManager();} finally {Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); | |
} | |
} |
接下来,进入 ReactInstanceManger 类看一下 createReactContextInBackground 方法,
/** | |
* Trigger react context initialization asynchronously in a background async task. This enables | |
* applications to pre-load the application JS, and execute global code before | |
* {@link ReactRootView} is available and measured. This should only be called the first time the | |
* application is set up, which is enforced to keep developers from accidentally creating their | |
* application multiple times without realizing it. | |
* | |
* Called from UI thread. | |
*/ | |
@ThreadConfined(UI) | |
public void createReactContextInBackground() {Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContextInBackground()"); | |
Assertions.assertCondition( | |
!mHasStartedCreatingInitialContext, | |
"createReactContextInBackground should only be called when creating the react" + | |
"application for the first time. When reloading JS, e.g. from a new file, explicitly" + | |
"use recreateReactContextInBackground"); | |
// 仅在应用首次启动是调用,防止开发人员意外的创建其他应用 | |
mHasStartedCreatingInitialContext = true; | |
recreateReactContextInBackgroundInner();} |
createReactContextInBackground 方法仅会在首次启动时调用,重新加载(reloaded)app 时,会调用 recreateReactContextInBackground(), 两个方法都会调用 recreateReactContextInBackgroundInner(),
@ThreadConfined(UI) | |
private void recreateReactContextInBackgroundInner() {Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackgroundInner()"); | |
PrinterHolder.getPrinter() | |
.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: recreateReactContextInBackground"); | |
//UI 线程 | |
UiThreadUtil.assertOnUiThread(); | |
// 开发模式,实现在线更新 Bundle,晃动弹出调试菜单等功能,这一部分属于调试功能流程。if (mUseDeveloperSupport && mJSMainModulePath != null) {final DeveloperSettings devSettings = mDevSupportManager.getDevSettings(); | |
// If remote JS debugging is enabled, load from dev server. | |
if (mDevSupportManager.hasUpToDateJSBundleInCache() && | |
!devSettings.isRemoteJSDebugEnabled()) { | |
// If there is a up-to-date bundle downloaded from server, | |
// with remote JS debugging disabled, always use that. | |
// 调试模式,从服务器加载 jsBundle | |
onJSBundleLoadedFromServer(null); | |
return; | |
} | |
if (!Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) { | |
// 加载服务 bundle | |
if (mBundleLoader == null) {mDevSupportManager.handleReloadJS(); | |
} else { | |
mDevSupportManager.isPackagerRunning(new PackagerStatusCallback() { | |
@Override | |
public void onPackagerStatusFetched(final boolean packagerIsRunning) { | |
UiThreadUtil.runOnUiThread(new Runnable() { | |
@Override | |
public void run() {if (packagerIsRunning) {mDevSupportManager.handleReloadJS(); | |
} else { | |
// If dev server is down, disable the remote JS debugging. | |
devSettings.setRemoteJSDebugEnabled(false); | |
recreateReactContextInBackgroundFromBundleLoader();} | |
} | |
}); | |
} | |
}); | |
} | |
return; | |
} | |
} | |
// 加载本地 bundle | |
recreateReactContextInBackgroundFromBundleLoader();} |
recreateReactContextInBackgroundFromBundleLoader 方法向下调用 recreateReactContextInBackground 方法
@ThreadConfined(UI) | |
private void recreateReactContextInBackground( | |
//C++ 和 JS 双向通信的中转站 | |
JavaScriptExecutorFactory jsExecutorFactory, | |
// bundle 加载器,根据 ReactNativeHost 中的配置决定从哪里加载 bundle 文件 | |
JSBundleLoader jsBundleLoader) {Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackground()"); | |
UiThreadUtil.assertOnUiThread(); | |
// 创建 ReactContextInitParams 对象 | |
final ReactContextInitParams initParams = new ReactContextInitParams( | |
jsExecutorFactory, | |
jsBundleLoader); | |
if (mCreateReactContextThread == null) { | |
// 在 newThread 实例化 ReactContext | |
runCreateReactContextOnNewThread(initParams); | |
} else {mPendingReactContextInitParams = initParams;} | |
} | |
//runCreateReactContextOnNewThread()方法中内容 | |
final ReactApplicationContext reactApplicationContext = | |
createReactContext(initParams.getJsExecutorFactory().create(), | |
initParams.getJsBundleLoader()); |
在 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。
private ReactApplicationContext createReactContext( | |
JavaScriptExecutor jsExecutor, | |
JSBundleLoader jsBundleLoader) {Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()"); | |
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START, jsExecutor.getName()); | |
// ReactApplicationContext 是 reactContext 的包装类 | |
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext); | |
NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null | |
? mNativeModuleCallExceptionHandler | |
: mDevSupportManager; | |
reactContext.setNativeModuleCallExceptionHandler(exceptionHandler); | |
// 创建 JavaModule 注册表 Builder,用来创建 JavaModule 注册表,JavaModule 注册表将所有的 JavaModule 注册到 CatalystInstance 中。NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false); | |
//jsExecutor、nativeModuleRegistry、nativeModuleRegistry 等各种参数处理好之后,开始构建 CatalystInstanceImpl 实例。CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder() | |
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()) | |
.setJSExecutor(jsExecutor)// js 执行通信类 | |
.setRegistry(nativeModuleRegistry)//java 模块注册表 | |
.setJSBundleLoader(jsBundleLoader)// bundle 加载器 | |
.setNativeModuleCallExceptionHandler(exceptionHandler); // 异常处理器 | |
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START); | |
// CREATE_CATALYST_INSTANCE_END is in JSCExecutor.cpp | |
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance"); | |
final CatalystInstance catalystInstance; | |
try {catalystInstance = catalystInstanceBuilder.build(); | |
} finally {Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); | |
ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END); | |
} | |
if (mJSIModulePackage != null) { | |
catalystInstance.addJSIModules(mJSIModulePackage | |
.getJSIModules(reactContext, catalystInstance.getJavaScriptContextHolder())); | |
} | |
if (mBridgeIdleDebugListener != null) {catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener); | |
} | |
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) { | |
// 调用 CatalystInstanceImpl 的 Native 方法把 Java Registry 转换为 Json,再由 C ++ 层传送到 JS 层。catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true"); | |
} | |
ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START); | |
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle"); | |
// 通过 CatalystInstance 开始加载 JS Bundle | |
catalystInstance.runJSBundle(); | |
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); | |
// 关联 ReacContext 与 CatalystInstance | |
reactContext.initializeWithInstance(catalystInstance); | |
return reactContext; | |
} |
createReactContext 方法中用 catalystInstance.runJSBundle() 来加载 JS bundle
@Override | |
public void runJSBundle() { | |
... 省略代码 | |
mJSBundleLoader.loadScript(CatalystInstanceImpl.this); | |
} |
查看 loadScript 方法,参数 JSBundleLoaderDelegate 接口的实现类 CatalystInstanceImpl, 我们假设调用了 loadScriptFromAssets 方法,
@Override | |
public void loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously) { | |
mSourceURL = assetURL; | |
jniLoadScriptFromAssets(assetManager, assetURL, loadSynchronously); | |
} | |
private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously); |
CatalystInstanceImpl.java 最终还是调用 C ++ 层的 CatalystInstanceImpl.cpp 去加载 JS Bundle。
接下来看一下 CatalystInstance 的实现类 CatalystInstanceImpl 的构造方法:
private CatalystInstanceImpl( | |
final ReactQueueConfigurationSpec reactQueueConfigurationSpec, | |
final JavaScriptExecutor jsExecutor, | |
final NativeModuleRegistry nativeModuleRegistry, | |
final JSBundleLoader jsBundleLoader, | |
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge."); | |
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstanceImpl"); | |
//Native 方法,用来创建 JNI 相关状态,并返回 mHybridData | |
mHybridData = initHybrid(); | |
//RN 中的三个线程:Native Modules Thread、JS Thread、UI Thread,都是通过 Handler 来管理的。mReactQueueConfiguration = ReactQueueConfigurationImpl.create( | |
reactQueueConfigurationSpec, | |
new NativeExceptionHandler()); | |
mBridgeIdleListeners = new CopyOnWriteArrayList<>(); | |
mNativeModuleRegistry = nativeModuleRegistry; | |
mJSModuleRegistry = new JavaScriptModuleRegistry(); | |
mJSBundleLoader = jsBundleLoader; | |
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; | |
mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread(); | |
mTraceListener = new JSProfilerTraceListener(this); | |
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); | |
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge"); | |
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeCxxBridge"); | |
//Native 方法,调用 initializeBridge()方法,并创建 BridgeCallback 实例,初始化 Bridge。initializeBridge(new BridgeCallback(this), | |
jsExecutor, | |
mReactQueueConfiguration.getJSQueueThread(), | |
mNativeModulesQueueThread, | |
mNativeModuleRegistry.getJavaModules(this), | |
mNativeModuleRegistry.getCxxModules()); | |
Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge"); | |
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); | |
mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext()); | |
} | |
// 在 C ++ 层初始化通信桥 ReactBridge | |
private native void initializeBridge( | |
ReactCallback callback, | |
JavaScriptExecutor jsExecutor, | |
MessageQueueThread jsQueue, | |
MessageQueueThread moduleQueue, | |
Collection<JavaModuleWrapper> javaModules, | |
Collection<ModuleHolder> cxxModules); |
参数解读:
- 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::jniLoadScriptFromAssets
void 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 文件:
void NativeToJsBridge::loadApplication( | |
std::unique_ptr<JSModulesUnbundle> unbundle, | |
std::unique_ptr<const JSBigString> startupScript, | |
std::string startupScriptSourceURL) { | |
// 获取一个 MessageQueueThread,探后在线程中执行一个 Task。runOnExecutorQueue( | |
m_mainExecutorToken, | |
[unbundleWrap=folly::makeMoveWrapper(std::move(unbundle)), | |
startupScript=folly::makeMoveWrapper(std::move(startupScript)), | |
startupScriptSourceURL=std::move(startupScriptSourceURL)] | |
(JSExecutor* executor) mutable {auto unbundle = unbundleWrap.move(); | |
if (unbundle) {executor->setJSModulesUnbundle(std::move(unbundle)); | |
} | |
//executor 从 runOnExecutorQueue()返回的 map 中取得,与 OnLoad 中的 JSCJavaScriptExecutorHolder 对应,也与 | |
//Java 中的 JSCJavaScriptExecutor 对应。它的实例在 JSIExecutor.cpp 中实现。executor->loadApplicationScript(std::move(*startupScript), | |
std::move(startupScriptSourceURL)); | |
}); | |
} | |
关于 unbundle 命令 | |
<unbundle 命令,使用方式和 bundle 命令完全相同。unbundle 命令是在 bundle 命令的基础上增加了一项功能,除了生成整合 JS 文件 index.android.bundle 外,还会 | |
生成各个单独的未整合 JS 文件(但会被优化),全部放在 js-modules 目录下,同时会生成一个名为 UNBUNDLE 的标识文件,一并放在其中。UNBUNDLE 标识文件的前 4 个字节 | |
固定为 0xFB0BD1E5,用于加载前的校验。 |
进入项目 node_modules/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp 进一步调用 JSIExecutor.cpp 的 loadApplicationScript()方法。
// 解释执行 JS | |
runtime_->evaluateJavaScript(std::make_unique<BigStringBuffer>(std::move(script)), sourceURL); | |
flush(); |
flushedQueueJS 支线的是 MessageQueue.js 的 flushedQueue()方法,此时 JS 已经被加载到队列中,等待 Java 层来驱动它。加载完 JS 后,返回 reactApplicationContext,我们继续跟进它的实现。
我们回到 ReactInstanceManager 类的 runCreateReactContextOnNewThread 方法中,看到 setupReactContext()方法,进入之后可以看到 attachRootViewToInstance(reactRoot)方法,进入后
private void attachRootViewToInstance(final ReactRoot reactRoot) {Log.d(ReactConstants.TAG, "ReactInstanceManager.attachRootViewToInstance()"); | |
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachRootViewToInstance"); | |
UIManager uiManagerModule = UIManagerHelper.getUIManager(mCurrentReactContext, reactRoot.getUIManagerType()); | |
@Nullable Bundle initialProperties = reactRoot.getAppProperties(); | |
final int rootTag = uiManagerModule.addRootView(reactRoot.getRootViewGroup(), | |
initialProperties == null ? | |
new WritableNativeMap() : Arguments.fromBundle(initialProperties), | |
reactRoot.getInitialUITemplate()); | |
reactRoot.setRootViewTag(rootTag); | |
// 启动入口 | |
reactRoot.runApplication(); | |
Systrace.beginAsyncSection( | |
TRACE_TAG_REACT_JAVA_BRIDGE, | |
"pre_rootView.onAttachedToReactInstance", | |
rootTag); | |
UiThreadUtil.runOnUiThread(new Runnable() { | |
@Override | |
public void run() { | |
Systrace.endAsyncSection(TRACE_TAG_REACT_JAVA_BRIDGE, "pre_rootView.onAttachedToReactInstance", rootTag); | |
reactRoot.onStage(ReactStage.ON_ATTACH_TO_INSTANCE); | |
} | |
}); | |
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); | |
} |
catalystInstance.getJSModule(AppRegistry.class)AppRegistry.class 是 JS 层暴露给 Java 层的接口方法,它的真正实现在 AppRegistry.js 里,AppRegistry.js 是运行所有 RN 应用的 JS 层入口,
runApplication(appKey: string, appParameters: any): void { | |
const msg = | |
'Running application"' + | |
appKey + | |
'"with appParams:' + | |
JSON.stringify(appParameters) + | |
'.' + | |
'__DEV__ ===' + | |
String(__DEV__) + | |
', development-level warning are' + | |
(__DEV__ ? 'ON' : 'OFF') + | |
', performance optimizations are' + | |
(__DEV__ ? 'OFF' : 'ON'); | |
infoLog(msg); | |
BugReporting.addSource( | |
'AppRegistry.runApplication' + runCount++, | |
() => msg,); | |
invariant(runnables[appKey] && runnables[appKey].run, | |
'Application' + | |
appKey + | |
'has not been registered.\n\n' + | |
"Hint: This error often happens when you're running the packager "+'(local dev server) from a wrong folder. For example you have '+'multiple apps and the packager is still running for the app you '+'were working on before.\nIf this is the case, simply kill the old '+'packager instance (e.g. close the packager terminal window) '+'and start the packager in the correct app folder (e.g. cd into app '+"folder and run 'npm start').\n\n"+'This error can also happen due to a require() error during '+'initialization or failure to call AppRegistry.registerComponent.\n\n',); | |
SceneTracker.setActiveScene({name: appKey}); | |
runnables[appKey].run(appParameters); | |
}, |
????,基本到这,就会去调用 JS 进行组件渲染,再通过 Java 层的 UIManagerModule 将 JS 组件转换为 Android 组件,最终显示在 ReactRootView 上,即完成启动过程。????
阅读源代码还是挺耗时的事情,哈哈。
同步更新至个人公众号及网站。
个人网站:https://wayne214.github.io
CSDN:https://blog.csdn.net/wayne214
公众号:君伟说。