RN SDK版本:0.63.2
安卓端RN启动流程分为启动和重启两个过程,启动不用多说,重启就是摇一摇出来的窗口中点击reload按钮后执行的流程。由于重启有明显的触发点,所以我选择从重启开始介绍。
一、安卓RN 重启过程
- 首先分析一下 handleReloadJS这个方法,这个在DevSupportManagerBase.java这个类里面,初次加载和重启时都会用到。
@Override
public void onPackagerReloadCommand() {
// Disable debugger to resume the JsVM & avoid thread locks while reloading
mDevServerHelper.disableDebugger();
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
handleReloadJS();
}
});
}
这个方法根据是否debug,会走不同分支:
1)debug时,走的是reloadJSInProxyMode, 这个方法会先创建JavaJSExecutor 然后执行ReactInstanceManager.java 的onReloadWithJSDebugger 方法;
2)没有debug 时,走的是reloadJSFromServer方法,这个方法会从服务器上下载bundle包,下载完之后执行ReactInstanceManager.java 的onJSBundleLoadedFromServer。
@Override
public void handleReloadJS() {
UiThreadUtil.assertOnUiThread();
ReactMarker.logMarker(
ReactMarkerConstants.RELOAD,
mDevSettings.getPackagerConnectionSettings().getDebugServerHost());
// dismiss redbox if exists
hideRedboxDialog();
if (mDevSettings.isRemoteJSDebugEnabled()) {
PrinterHolder.getPrinter()
.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Proxy");
mDevLoadingViewController.showForRemoteJSEnabled();
mDevLoadingViewVisible = true;
reloadJSInProxyMode();
} else {
PrinterHolder.getPrinter()
.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Server");
String bundleURL =
mDevServerHelper.getDevServerBundleURL(Assertions.assertNotNull(mJSAppBundleName));
reloadJSFromServer(bundleURL);
}
}
2.onJSBundleLoadedFromServer方法是加载本地缓存的临时包,其中临时包路径通过getDownloadedJSBundleFile这个接口方法获取,这个方法的实现在DevSupportManagerBase.java里面,临时包名称为ReactNativeDevBundle.js
@ThreadConfined(UI)
private void onJSBundleLoadedFromServer() {
FLog.d(ReactConstants.TAG, "ReactInstanceManager.onJSBundleLoadedFromServer()");
JSBundleLoader bundleLoader =
JSBundleLoader.createCachedBundleFromNetworkLoader(
mDevSupportManager.getSourceUrl(), mDevSupportManager.getDownloadedJSBundleFile());
recreateReactContextInBackground(mJavaScriptExecutorFactory, bundleLoader);
}
private static final String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js";
二、安卓 RN 启动过程
1.先从 RNSingleInstance 的 init方法开始 ,这是我们自己写的类,主要是做添加module package,创建RN bridge,以及创建和重启bridge 过程的监听等,这里面调用了 createReactContextInBackground来创建RN环境。
2.createReactContextInBackground
这个逻辑很简单,只是根据一个bool 变量是否为空来控制后面的recreateReactContextInBackgroundInner方法只走一次。
@ThreadConfined(UI)
public void createReactContextInBackground() {
FLog.d(TAG, "ReactInstanceManager.createReactContextInBackground()");
UiThreadUtil
.assertOnUiThread(); // Assert before setting mHasStartedCreatingInitialContext = true
if (!mHasStartedCreatingInitialContext) {
mHasStartedCreatingInitialContext = true;
recreateReactContextInBackgroundInner();
}
}
3.recreateReactContextInBackgroundInner
这里面会根据mBundleLoader是否为空,来执行不同分支的逻辑,如果mBundleLoader为空,会走handleRealodJS 方法,重新创建mBundleLoader(因为mBundleLoader 在builder方法内就会被赋值,所以实际运行时没有走到过这个分支),正常情况下mBundleLoader不为空,会在一个runnable的 匿名线程里面执行bundle的加载。
加载的逻辑分为三个分支:
1)如果RN包正在运行,会调用handleReloadJS重新加载bridge,这里面会根据是否是debug模式来走对应的分支
2)如果缓存包已经是最新的,并且没有开启debug模式,则调用onJSBundleLoadedFromServer 从本地指定路径加载 RN包
3)其他情况,这时候应该是服务器挂掉了,需要禁用debug模式并调用 recreateReactContextInBackgroundFromBundleLoader方法重新创建bridge。
不管上面哪种情况,都会走recreateReactContextInBackgroundFromBundleLoader 加载bundle。
@ThreadConfined(UI)
private void recreateReactContextInBackgroundInner() {
FLog.d(TAG, "ReactInstanceManager.recreateReactContextInBackgroundInner()");
PrinterHolder.getPrinter()
.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: recreateReactContextInBackground");
UiThreadUtil.assertOnUiThread();
if (mUseDeveloperSupport && mJSMainModulePath != null) {
final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();
if (!Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
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 (mDevSupportManager.hasUpToDateJSBundleInCache()
&& !devSettings.isRemoteJSDebugEnabled()) {
// If there is a up-to-date bundle downloaded from server,
// with remote JS debugging disabled, always use that.
onJSBundleLoadedFromServer();
} else {
// If dev server is down, disable the remote JS debugging.
devSettings.setRemoteJSDebugEnabled(false);
recreateReactContextInBackgroundFromBundleLoader();
}
}
});
}
});
}
return;
}
}
recreateReactContextInBackgroundFromBundleLoader();
}
4.recreateReactContextInBackgroundFromBundleLoader.
这个方法打印了一行log,实际执行的是recreateReactContextInBackground,而这个方法内部会根据mCreateReactContextThread是否为空,来执行不同分支: 如果为空,则调用 mCreateReactContextThread方法,否则根据传进来的jsExecutorFactory和 jsBundleLoader来创建mPendingReactContextInitParams, 这个是用于重新创建bridge 所需的参数。
@ThreadConfined(UI)
private void recreateReactContextInBackgroundFromBundleLoader() {
FLog.d(TAG, "ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()");
PrinterHolder.getPrinter()
.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from BundleLoader");
recreateReactContextInBackground(mJavaScriptExecutorFactory, mBundleLoader);
}
- RunCreateReactContextOnNewThread
这个方法启用了一个新的runnable线程来创建RN环境,如果此时正在销毁bridge,则等待销毁完成再执行后面的操作。然后根据前面创建的initParams来构造 JavaScriptExecutor,再把JavaScriptExecutor和JSBundleLoader 传入到createReactContext方法里面来执行最后的加载。
@ThreadConfined(UI)
private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
FLog.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()");
UiThreadUtil.assertOnUiThread();
synchronized (mAttachedReactRoots) {
synchronized (mReactContextLock) {
if (mCurrentReactContext != null) {
tearDownReactContext(mCurrentReactContext);
mCurrentReactContext = null;
}
}
}
mCreateReactContextThread =
new Thread(
null,
new Runnable() {
@Override
public void run() {
ReactMarker.logMarker(REACT_CONTEXT_THREAD_END);
synchronized (ReactInstanceManager.this.mHasStartedDestroying) {
while (ReactInstanceManager.this.mHasStartedDestroying) {
try {
ReactInstanceManager.this.mHasStartedDestroying.wait();
} catch (InterruptedException e) {
continue;
}
}
}
// As destroy() may have run and set this to false, ensure that it is true before we
// create
mHasStartedCreatingInitialContext = true;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
ReactMarker.logMarker(VM_INIT);
JavaScriptExecutor javaScriptExecuto = null;
try {
javaScriptExecuto = initParams.getJsExecutorFactory().create();
} catch (Throwable e) {
mSoInitSucess = false;
return;
}
final ReactApplicationContext reactApplicationContext =
createReactContext(javaScriptExecuto,
initParams.getJsBundleLoader());
mCreateReactContextThread = null;
ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_START);
final Runnable maybeRecreateReactContextRunnable =
new Runnable() {
@Override
public void run() {
if (mPendingReactContextInitParams != null) {
runCreateReactContextOnNewThread(mPendingReactContextInitParams);
mPendingReactContextInitParams = null;
}
}
};
Runnable setupReactContextRunnable =
new Runnable() {
@Override
public void run() {
try {
setupReactContext(reactApplicationContext);
} catch (Exception e) {
// TODO T62192299: remove this after investigation
FLog.e(
ReactConstants.TAG,
"ReactInstanceManager caught exception in setupReactContext",
e);
mDevSupportManager.handleException(e);
}
}
};
reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable);
UiThreadUtil.runOnUiThread(maybeRecreateReactContextRunnable);
} catch (Exception e) {
mDevSupportManager.handleException(e);
}
}
},
"create_react_context");
ReactMarker.logMarker(REACT_CONTEXT_THREAD_START);
mCreateReactContextThread.start();
}
这里提一下ReactMarker 这个类,是用来记录RN加载过程的一些关键节点的时间(具体参见ReactMarkerConstants ),主要用来做性能检测,里面也会发通知,可以用于获取流程中的各个关键节点。比如说我们项目里监听页面加载完成的通知,来控制loading的显示和隐藏。
ReactMarker.addListener(new ReactMarker.MarkerListener() {
@Override
public void logMarker(ReactMarkerConstants name, @Nullable String tag, int instanceKey) {
if(ReactMarkerConstants.CONTENT_APPEARED==name){
Activity topActivity = RnActivityStackManager.getInstance().getTopActivity();
if (topActivity instanceof RNActivity) {
((RNActivity) topActivity).dismissLoading();
}
}
}
});
6.createReactContext
这里面根据传入的 JavaScriptExecutor和JSBundleLoader等参数构造一个CatalystInstanceImpl, 这个是对C++ 层CatalystInstanceImpl 的封装,后面还有一些监听加载的逻辑。CatalystInstanceImpl 里面都是C++ 代码,大部分逻辑都是与iOS共用的,参考iOS C++部分的加载逻辑即可。
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
FLog.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()");
ReactMarker.logMarker(CREATE_REACT_CONTEXT_START, jsExecutor.getName());
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
NativeModuleCallExceptionHandler exceptionHandler =
mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
reactContext.setNativeModuleCallExceptionHandler(exceptionHandler);
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
CatalystInstanceImpl.Builder catalystInstanceBuilder =
new CatalystInstanceImpl.Builder()
.setContext(mApplicationContext)
.setExceptionReorters(mExceptionReorters)
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSBundleLoader(jsBundleLoader)
.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);
}
reactContext.initializeWithInstance(catalystInstance);
// TODO(T46487253): Remove after task is closed
FLog.e(
ReactConstants.TAG,
"ReactInstanceManager.createReactContext: mJSIModulePackage "
+ (mJSIModulePackage != null ? "not null" : "null"));
if (mJSIModulePackage != null) {
catalystInstance.addJSIModules(
mJSIModulePackage.getJSIModules(
reactContext, catalystInstance.getJavaScriptContextHolder()));
// TODO(T46487253): Remove after task is closed
FLog.e(
ReactConstants.TAG,
"ReactInstanceManager.createReactContext: ReactFeatureFlags.useTurboModules == "
+ (ReactFeatureFlags.useTurboModules == false ? "false" : "true"));
if (ReactFeatureFlags.useTurboModules) {
JSIModule turboModuleManager =
catalystInstance.getJSIModule(JSIModuleType.TurboModuleManager);
// TODO(T46487253): Remove after task is closed
FLog.e(
ReactConstants.TAG,
"ReactInstanceManager.createReactContext: TurboModuleManager "
+ (turboModuleManager == null ? "not created" : "created"));
catalystInstance.setTurboModuleManager(turboModuleManager);
TurboModuleRegistry registry = (TurboModuleRegistry) turboModuleManager;
// Eagerly initialize TurboModules
for (String moduleName : registry.getEagerInitModuleNames()) {
registry.getModule(moduleName);
}
}
}
if (mBridgeIdleDebugListener != null) {
catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
}
if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
}
ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
catalystInstance.runJSBundle();
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
return reactContext;
}
- setupReactContext等
前面调用createReactContext方法之后,还有一些后续操作,如setupReactContext , 以及RN环境创建的重试,setupReactContext里面执行了诸如catalystInstance初始化,attachRootViewToInstance等方法,为RN代码的执行做好准备。 RN环境创建的重试具体逻辑为: 判断 mPendingReactContextInitParams是否为空,不为空则重新调用RunCreateReactContextOnNewThread(第5步) ,调用成功后再把mPendingReactContextInitParams置空
private void setupReactContext(final ReactApplicationContext reactContext) {
FLog.d(ReactConstants.TAG, "ReactInstanceManager.setupReactContext()");
ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_END);
ReactMarker.logMarker(SETUP_REACT_CONTEXT_START);
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext");
synchronized (mAttachedReactRoots) {
synchronized (mReactContextLock) {
mCurrentReactContext = Assertions.assertNotNull(reactContext);
}
CatalystInstance catalystInstance =
Assertions.assertNotNull(reactContext.getCatalystInstance());
catalystInstance.initialize();
mDevSupportManager.onNewReactContextCreated(reactContext);
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
moveReactContextToCurrentLifecycleState();
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
for (ReactRoot reactRoot : mAttachedReactRoots) {
attachRootViewToInstance(reactRoot);
}
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_END);
}
// There is a race condition here - `finalListeners` can contain null entries
// See usage below for more details.
ReactInstanceEventListener[] listeners =
new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
final ReactInstanceEventListener[] finalListeners =
mReactInstanceEventListeners.toArray(listeners);
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
for (ReactInstanceEventListener listener : finalListeners) {
// Sometimes this listener is null - probably due to race
// condition between allocating listeners with a certain
// size, and getting a `final` version of the array on
// the following line.
if (listener != null) {
listener.onReactContextInitialized(reactContext);
}
}
}
});
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(SETUP_REACT_CONTEXT_END);
reactContext.runOnJSQueueThread(
new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
ReactMarker.logMarker(CHANGE_THREAD_PRIORITY, "js_default");
}
});
reactContext.runOnNativeModulesQueueThread(
new Runnable() {
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
}
});
}