從 ReactRootView 角度理解React-Native源碼

眾所周知,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)的操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容