React Native源码分析(一):环境初始化以及Bundle加载
前言
React Native源码分析是一个系列文章,包含三个部分, (1)初始化以及Bundle加载流程 (2)js和native的通信 (3)js组件在native侧是如何展示
打开一个RN页面
通过RN官网中的文档创建一个demo工程,在里面看一下是如何打开RN页面的
从流程图上看,最终进行RN环境初始化以及打开RN页面是在ReactRootView中进行的。 在ReactRootView中有一个
startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle initialProperties)
方法,这里就是我们要分析的入口
流程分析
通过分析源码我认为初始化流程上有三个阶段比较重要
(1)ReactInstanceManager的创建
(2)ReactContext的创建
(3)ReactContext的装配
ReactInstanceManager的创建
ReactInstanceManager的创建在模板工程里其实是在ReactNativeHost进行进行的,
protected ReactInstanceManager createReactInstanceManager() {
ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
ReactInstanceManagerBuilder builder =
ReactInstanceManager.builder()
.setApplication(mApplication)
.setJSMainModulePath(getJSMainModuleName())
.setUseDeveloperSupport(getUseDeveloperSupport())
.setDevSupportManagerFactory(getDevSupportManagerFactory())
.setRequireActivity(getShouldRequireActivity())
.setSurfaceDelegateFactory(getSurfaceDelegateFactory())
.setRedBoxHandler(getRedBoxHandler())
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
.setUIImplementationProvider(getUIImplementationProvider())
.setJSIModulesPackage(getJSIModulePackage())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE)
.setReactPackageTurboModuleManagerDelegateBuilder(
getReactPackageTurboModuleManagerDelegateBuilder());
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
ReactInstanceManager reactInstanceManager = builder.build();
ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_END);
return reactInstanceManager;
}
这个ReactInstanceManager是RN的一个核心类,包含了所有配置,这里我们目光聚焦到
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
public ReactInstanceManagerBuilder setJSBundleFile(String jsBundleFile) {
if (jsBundleFile.startsWith("assets://")) {
mJSBundleAssetUrl = jsBundleFile;
mJSBundleLoader = null;
return this;
}
return setJSBundleLoader(JSBundleLoader.createFileLoader(jsBundleFile));
}
public ReactInstanceManagerBuilder setBundleAssetName(String bundleAssetName) {
mJSBundleAssetUrl = (bundleAssetName == null ? null : "assets://" + bundleAssetName);
mJSBundleLoader = null;
return this;
}
jsBundleFile代表要加载的bundle路径,如果这个路径为空或者放在assets目录下,那么就会设置mJSBundleAssetUrl为bundle文件名,一般命名为(android.index.bundle),否则就会创建一个 JSBundleLoader,这个loader用来从文件加载bundle,后续在ReactContext的装配中会再提到。
ReactContext的创建
startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle initialProperties){
...
mReactInstanceManager.createReactContextInBackground();
...
}
ReactRootView的startReactApplication需要传入一个ReactInstanceManager,就是上一节构建的ReactInstanceManager。之后会调用ReactInstanceManager的createReactContextInBackground方法,顺着createReactContextInBackground方法逐级跟踪下去,发现最终会调用到ReactInstanceManager的runCreateReactContextOnNewThread(final ReactContextInitParams initParams)方法
@ThreadConfined(UI)
private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
...
mCreateReactContextThread =
new Thread(
null,
new Runnable() {
@Override
public void run() {
...
try {
...
//创建ReactContext
reactApplicationContext =
createReactContext(
initParams.getJsExecutorFactory().create(),
initParams.getJsBundleLoader());
} catch (Exception e) {
...
return;
}
...
//装在ReactContext
Runnable setupReactContextRunnable =
new Runnable() {
@Override
public void run() {
try {
setupReactContext(reactApplicationContext);
} catch (Exception e) {
...
}
}
};
reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable);
...
} catch (Exception e) {
...
}
}
},
"create_react_context");
...
mCreateReactContextThread.start();
}
可以看到在runCreateReactContextOnNewThread方法中会创建一个线程mCreateReactContextThread,在这个线程里会调用createReactContext进行ReactContext的创建,看一下createReactContext方法
/** @return instance of {@link ReactContext} configured a {@link CatalystInstance} set */
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
...
//1.创建ReactApplicationContext实例
final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
NativeModuleCallExceptionHandler exceptionHandler =
mNativeModuleCallExceptionHandler != null
? mNativeModuleCallExceptionHandler
: mDevSupportManager;
reactContext.setNativeModuleCallExceptionHandler(exceptionHandler);
//2.处理ReactPackage
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
CatalystInstanceImpl.Builder catalystInstanceBuilder =
new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
...
try {
//3.创建CatalystInstance实例
catalystInstance = catalystInstanceBuilder.build();
} finally {
...
}
//4.ReactContext根据CatalystInstance进行初始化
reactContext.initializeWithInstance(catalystInstance);
...
//5执行jsbundle
catalystInstance.runJSBundle();
return reactContext;
}
这个方法主要有五个部分,首先创建ReactApplicationContext实例,也是这个方法的返回值,然后去处理ReactPackage,这里会放到下期再说,先略过,接着是创建CatalystInstance实例,CatalystInstance主要用来初始化和js通信的桥,在native侧调用js侧方法,而且在构造函数里会创建native侧调用js方法的线程,js侧调用native方法的线程,以及UI线程,他们都是MessageQueueThreadImpl类的实例,这几个线程都有一个Looper。
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry nativeModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
...
//创建native线程,js线程以及ui线程
mReactQueueConfiguration =
ReactQueueConfigurationImpl.create(
reactQueueConfigurationSpec, new NativeExceptionHandler());
...
//初始化和js通信的桥
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mNativeModulesQueueThread,
mNativeModuleRegistry.getJavaModules(this),
mNativeModuleRegistry.getCxxModules());
...
}
接着调用reactContext.initializeWithInstance(catalystInstance);传入CatalystInstance到ReactContext进行配置,获取CatalystInstance中ReactQueueConfiguration的线程。
/** Set and initialize CatalystInstance for this Context. This should be called exactly once. */
public void initializeWithInstance(CatalystInstance catalystInstance) {
...
ReactQueueConfiguration queueConfig = catalystInstance.getReactQueueConfiguration();
initializeMessageQueueThreads(queueConfig);
}
/** Initialize message queue threads using a ReactQueueConfiguration. */
public synchronized void initializeMessageQueueThreads(ReactQueueConfiguration queueConfig) {
FLog.w(TAG, "initializeMessageQueueThreads() is called.");
if (mUiMessageQueueThread != null
|| mNativeModulesMessageQueueThread != null
|| mJSMessageQueueThread != null) {
throw new IllegalStateException("Message queue threads already initialized");
}
mUiMessageQueueThread = queueConfig.getUIQueueThread();
mNativeModulesMessageQueueThread = queueConfig.getNativeModulesQueueThread();
mJSMessageQueueThread = queueConfig.getJSQueueThread();
/** TODO(T85807990): Fail fast if any of the threads is null. */
if (mUiMessageQueueThread == null) {
throw new IllegalStateException("UI thread is null");
}
if (mNativeModulesMessageQueueThread == null) {
throw new IllegalStateException("NativeModules thread is null");
}
if (mJSMessageQueueThread == null) {
throw new IllegalStateException("JavaScript thread is null");
}
mIsInitialized = true;
}
最后就是调用catalystInstance.runJSBundle()加载bundle
@Override
public void runJSBundle() {
...
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
...
}
这里的mJSBundleLoader和ReactInstanceManager构建时传入的JSBundleLoader是一样的,我们在demo里没有传入JSBundleLoader,这里会使用默认的JSBundleLoader
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;
}
};
}
可以看到loadScipt其实使用的就是CatalystInstanceImpl的loadScriptFromAssets方法,CatalystInstanceImpl就是CatalystInstance的实现类
@Override
public void loadScriptFromAssets(
AssetManager assetManager, String assetURL, boolean loadSynchronously) {
mSourceURL = assetURL;
jniLoadScriptFromAssets(assetManager, assetURL, loadSynchronously);
}
可以看到CatalystInstanceImpl中的loadScriptFromAssets是通过C++方法进行的bundle加载的调用,这里不再详述。至此ReactContext的创建和bundle的加载就完成了。
ReactContext的装配
ReactContext的装配是在ReactInstanceManager里进行的
private void setupReactContext(final ReactApplicationContext reactContext) {
...
CatalystInstance catalystInstance =
Assertions.assertNotNull(reactContext.getCatalystInstance());
catalystInstance.initialize();
mDevSupportManager.onNewReactContextCreated(reactContext);
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
for (ReactRoot reactRoot : mAttachedReactRoots) {
if (reactRoot.getState().compareAndSet(ReactRoot.STATE_STOPPED, ReactRoot.STATE_STARTED)) {
attachRootViewToInstance(reactRoot);
}
}
...
UiThreadUtil.runOnUiThread(
new Runnable() {
@Override
public void run() {
moveReactContextToCurrentLifecycleState();
for (com.facebook.react.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);
}
}
}
});
...
}
至此整个环境初始化过程结束,还会回调onReactContextInitialized()方法,其中还有
一个是CatalystInstance的initialize方法,这个方法可以用来实现NativeModules的初始化,然后找到创建的ReactRootView,执行它的attachRootViewToInstance
private void attachRootViewToInstance(final ReactRoot reactRoot) {
FLog.d(ReactConstants.TAG, "ReactInstanceManager.attachRootViewToInstance()");
Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachRootViewToInstance");
@Nullable
UIManager uiManager =
UIManagerHelper.getUIManager(mCurrentReactContext, reactRoot.getUIManagerType());
// If we can't get a UIManager something has probably gone horribly wrong
if (uiManager == null) {
throw new IllegalStateException(
"Unable to attach a rootView to ReactInstance when UIManager is not properly"
+ " initialized.");
}
@Nullable Bundle initialProperties = reactRoot.getAppProperties();
final int rootTag;
...
rootTag =
uiManager.addRootView(
reactRoot.getRootViewGroup(),
initialProperties == null
? new WritableNativeMap()
: Arguments.fromBundle(initialProperties),
reactRoot.getInitialUITemplate());
reactRoot.setRootViewTag(rootTag);
reactRoot.runApplication();
...
}
最终会调用到ReactRootView的runApplication方法
public void runApplication() {
try {
if (mReactInstanceManager == null || !mIsAttachedToInstance) {
return;
}
ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
if (reactContext == null) {
return;
}
CatalystInstance catalystInstance = reactContext.getCatalystInstance();
String jsAppModuleName = getJSModuleName();
if (mWasMeasured) {
updateRootLayoutSpecs(true, mWidthMeasureSpec, mHeightMeasureSpec);
}
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", getRootViewTag());
@Nullable Bundle appProperties = getAppProperties();
if (appProperties != null) {
appParams.putMap("initialProps", Arguments.fromBundle(appProperties));
}
mShouldLogContentAppeared = true;
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
CatalystInstance会通过动态代理调用声明AppRegistry接口中的方法,最终该runApplication就会映射调用到js侧代码的同名函数
// AppRegistry.js
runApplication(
appKey: string,
appParameters: any,
displayMode?: number,
): void {
if (appKey !== 'LogBox') {
const logParams = __DEV__
? '" with ' + JSON.stringify(appParameters)
: '';
const msg = 'Running "' + appKey + logParams;
infoLog(msg);
BugReporting.addSource(
'AppRegistry.runApplication' + runCount++,
() => msg,
);
}
invariant(
runnables[appKey] && runnables[appKey].run,
`"${appKey}" has not been registered. This can happen if:\n` +
'* Metro (the local dev server) is run from the wrong folder. ' +
'Check if Metro is running, stop it and restart it in the current project.\n' +
"* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called.",
);
SceneTracker.setActiveScene({name: appKey});
runnables[appKey].run(appParameters, displayMode);
},
执行AppRegistery的renderApplication方法进行组件的渲染。
关注我的公众号:滑板上的老砒霜
转载自:https://juejin.cn/post/7116866204467200007