眾所周知,React-Native 是一款優(yōu)秀的 JavaScript 框架,它將 React 和原生相結(jié)合,將 React 組件渲染為原生平臺的組件,這意味著性能和原生控件幾無二別。它可以用JSX,混編js、css、html,這大大提高了開發(fā)的效率,開發(fā)者只關(guān)心如何用 JavaScript 構(gòu)造頁面。而它的高效之處在于獨創(chuàng)的 Virtual DOM,Virtual DOM是存在于內(nèi)存中的 JavaScript 對象,它和 DOM 是一一對應(yīng)的關(guān)系,得益于高效的 DOM-diff 算法,當界面發(fā)生變化的時候,Virtual DOM 會高效的更改 DOM 從而避免了頻繁的繪制,提高了性能。
但是,在現(xiàn)實的使用場景之中,我們常常混合開發(fā),而不會純粹的使用 React-Native,這是由于,雖然 FaceBook 官方提供了大量的組件,但是畢竟還是有很多業(yè)務(wù)上的或者是代碼邏輯上的需求是無法滿足的,這就需要兩端另外去開發(fā)對應(yīng)的組件,反而增大了開發(fā)成本。除此之外,作為至關(guān)重要的列表,F(xiàn)latList 的表現(xiàn)差強人意,只能夠展現(xiàn)相對簡單的列表,而圖片過多,或者內(nèi)容相對復(fù)雜,就會導(dǎo)致明顯的掉幀和卡頓。
下面將從 ReactRootView 的角度來理解 React-Native 的加載過程。
我們在使用一個 React-Native 頁面的時候,有兩種實現(xiàn)方式,一種是繼承官方的 ReactActivity ,另一種是把 ReactRootView 添加進布局。
第一種方式相對簡單。這兩種發(fā)方法大同小異,只不過第二種方法相對靈活,但是需要自己去掛載和卸載組件。當我們跟蹤 ReactActivityDelegate 的代碼,會發(fā)現(xiàn)實現(xiàn)掛載的調(diào)用最終走向了這個方法:
public void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);}
mReactRootView.startReactApplication 方法即是用來啟動JS應(yīng)用,并且渲染由js組件。繼續(xù)往下看:
public void startReactApplication(
ReactInstanceManager reactInstanceManager,
String moduleName,
@Nullable Bundle initialProperties,
@Nullable String initialUITemplate) {
try {
UiThreadUtil.assertOnUiThread();
mReactInstanceManager = reactInstanceManager;
mJSModuleName = moduleName;
mAppProperties = initialProperties;
mInitialUITemplate = initialUITemplate;
mReactInstanceManager.createReactContextInBackground();
attachToReactInstanceManager();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
這里去掉了冗余的代碼,只留下關(guān)鍵性代碼,createReactContextInBackground() 方法是在子線程中創(chuàng)建 ReactContext ,之后會調(diào)用 recreateReactContextInBackgroundInner() 方法,這里需要用到強大的 ReactInstanceManager 類,ReactInstanceManager 是一個比較核心的類,它管理著 ReactRootView 的生命周期,管理著 js 的加載,管理著Native和js 的交互,管理著初始化的參數(shù)等等。
這里我們只關(guān)心 js 的加載和View 的掛載。一路追蹤下去,執(zhí)行的是
recreateReactContextInBackgroundFromBundleLoader()方法,這段關(guān)鍵代碼是: recreateReactContextInBackground(mJavaScriptExecutorFactory, mBundleLoader);
而 mJavaScriptExecutorFactory 和 mBundleLoader 是在 類,ReactInstanceManager 初始化的時候已經(jīng)準備就緒了,這兩個類就是用來執(zhí)行js文件的,JavaScriptExecutor 的作用是調(diào)用Native方法initHybrid()初始化C++層RN與JSC通信的框架,JSBundleLoader 是通過不同的場景去創(chuàng)建不同的加載器,用以加載js文件,這兩個類會封裝為 ReactContextInitParams 傳遞給核心的方法: runCreateReactContextOnNewThread(),在這這個線程中有兩個關(guān)鍵的部分,一個是創(chuàng)建 ReactContext, 一個是 setupReactContext。
首先看下createReactContext():
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()
.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;
}
在這個方法中,主要是 ReactContet 的初始化,而ReactContet 僅僅繼承自 ContextWrapper 而已,但是經(jīng)過官方的擴展,它承擔了 JS 和原生溝通的橋梁。
NativeModuleRegistry 通過 processPackages()方法 整合了項目中所有的的封裝組件或者是交互類,即 NativeModule , 按照官方的寫法,封裝成為 ReactPackage, 交由 CatalystInstance 去綁定。
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
而原生和 js 的交互正是由此而來,比如這樣的一段代碼:
const TextInANest = () => {
return (
<Text style={styles.baseText}>
<Text style={styles.titleText} onPress={onPressTitle}>
{titleText}
{"\n"}
{"\n"}
</Text>
<Text numberOfLines={5}>{bodyText}</Text>
</Text>
);
};
Text 組件擁有自己的屬性和方法,我們知道,所有的 React 組件最終會映射為原生組件,那么在原生和 js 之間必然維護了一個映射關(guān)系,當我們?nèi)ゲ榭?ReactTextViewManager 和它的父類 ReactTextAnchorViewManager 就會發(fā)現(xiàn)幾乎所有的屬性。首先,React Native將代碼由JSX轉(zhuǎn)化為JS組件,啟動過程中利用instantiateReactComponent將ReactElement轉(zhuǎn)化為復(fù)合組件ReactCompositeComponent與元組件ReactNativeBaseComponent,利用
ReactReconciler對他們進行渲染。UIManager.js利用C++層的Instance.cpp將UI信息傳遞給UIManagerModule.java,并利用UIManagerModule.java構(gòu)建UI。UIManagerModule.java接收到UI信息后,將UI的操作封裝成對應(yīng)的Action,放在隊列中等待執(zhí)行。各種UI的操作,例如創(chuàng)建、銷毀、更新等便在隊列里完成,UI最終得以渲染在屏幕上。而這個 ReactTextViewManager 最終是通過 ReactPackage 傳遞給NativeModuleRegistry 的作用就是把這些封裝的View或者交互的 Moudules 進行注冊,銷毀,刷新等工作,與之相對應(yīng)的js管理類是 JavaScriptModuleRegistry,這樣一來,就為下一步二者之間的交互搭建好基礎(chǔ)。
CatalystInstanceImpl 類在初始化的過程中,通過 ReactInstanceManager 把相應(yīng)的參數(shù)帶了進來,并初始化出三個 MessageQueueThread ,MessageQueueThread 只是一個接口,方便接受 Runable 進行調(diào)用,之后通過 CatalystInstance 的 runJSBundle() 方法加載 js 文件,在 CatalystInstanceImpl 實現(xiàn)類中調(diào)用 mJSBundleLoader.loadScript(CatalystInstanceImpl.this) 方法,
public void initializeMessageQueueThreads(ReactQueueConfiguration queueConfig) {
if (mUiMessageQueueThread != null
|| mNativeModulesMessageQueueThread != null
|| mJSMessageQueueThread != null) {
throw new IllegalStateException("Message queue threads already initialized");
}
mUiMessageQueueThread = queueConfig.getUIQueueThread();
mNativeModulesMessageQueueThread = queueConfig.getNativeModulesQueueThread();
mJSMessageQueueThread = queueConfig.getJSQueueThread(); }
這里初始化三個調(diào)度Runable 的類,分別是本地線程,js線程,ui線程,以方便交互。 至此,js已經(jīng)加載,而且Native 和js 的橋梁也搭建完成。
回頭看 setupReactContext 方法,它的主要作用是管理ReactRootView 的生命周期,在一個項目中,可能存在多個 ReactRootView,這里通過遍歷的方式,執(zhí)行 attachRootViewToInstance(reactRoot)方法,同時在ReactContext 創(chuàng)建完成的回調(diào)中,初始化一些插件,主要內(nèi)容在 ReactNativeFlipper 當中,暫不贅述,關(guān)鍵的代碼在 attachRootViewToInstance 當中:
private void attachRootViewToInstance(final ReactRoot reactRoot) {
// TODO: downgrade back to FLog.d once T62192299 is resolved.
FLog.e(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 =
uiManager.addRootView(
reactRoot.getRootViewGroup(),
initialProperties == null
? new WritableNativeMap()
: Arguments.fromBundle(initialProperties),
reactRoot.getInitialUITemplate());
reactRoot.setRootViewTag(rootTag);
if (reactRoot.getUIManagerType() == FABRIC) {
// Fabric requires to call updateRootLayoutSpecs before starting JS Application,
// this ensures the root will hace the correct pointScaleFactor.
uiManager.updateRootLayoutSpecs(
rootTag, reactRoot.getWidthMeasureSpec(), reactRoot.getHeightMeasureSpec());
reactRoot.setShouldLogContentAppeared(true);
} else {
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);
}
其主要的作用就是通過 UIManager 去渲染控件,至此完整的啟動流程已經(jīng)結(jié)束。
總結(jié):
ReactContext:ReactContext繼承于ContextWrapper,是ReactNative應(yīng)用的上下文,通過getContext()去獲得,通過它可以訪問ReactNative核心類的實現(xiàn)。
ReactInstanceManager:ReactInstanceManager是ReactNative應(yīng)用總的管理類,創(chuàng)建ReactContext、CatalystInstance等類,解析ReactPackage生成映射表,并且配合ReactRootView管理View的創(chuàng)建與生命周期等功能。
CatalystInstance:CatalystInstance是ReactNative應(yīng)用Java層、C++層、JS層通信總管理類,總管Java層、JS層核心Module映射表與回調(diào),三端通信的入口與橋梁。
JavaScriptModule:JavaScriptModule是JS Module,負責JS到Java的映射調(diào)用格式聲明,由CatalystInstance統(tǒng)一管理。
NativeModule:NativeModule是Java Module,負責Java到Js的映射調(diào)用格式聲明,由CatalystInstance統(tǒng)一管理。
JavascriptModuleRegistry:JavascriptModuleRegistry是JS Module映射表,NativeModuleRegistry是Java Module映射表。
UIManager:主要處理UI的渲染,JS層通過C++層把創(chuàng)建View的請求發(fā)送給Java層的UIManagerModule。UIManagerModule通過UIImplentation對操作請求進行包裝。
包裝后的操作請求被發(fā)送到View處理隊列UIViewOperationQueue隊列中等待處理。實際處理View時,根據(jù)class name查詢對應(yīng)的ViewNManager,然后調(diào)用原生View的方法對View進行相應(yīng)的操作。