React Native Android 原始碼分析之啟動過程

騎摩托馬斯發表於2018-07-09

前言

閱讀之前請先檢視準備工作

這篇開始將分析 React Native 0.56-stable 分支 在 Android 端的啟動過程是怎麼樣。

用過 React Native 的小夥伴都知道,React Native 是可以使用 JS 來編寫一份程式碼,並可以同時跑在 Android 和 iOS 兩個平臺上。那其實核心思想就是 JS 跟 Android 之間建立起一種通訊機制。

對於 Android 呼叫 JS 程式碼的方法有以下兩種:

  • 通過 WebViewloadUrl() 函式
  • 通過 WebViewevaluateJavascript() 函式

對於 JS 呼叫 Android 程式碼的方法有以下三種:

  • 通過 WebViewaddJavascriptInterface()進行物件對映
  • 通過 WebViewClientshouldOverrideUrlLoading() 方法回撥攔截 url
  • 通過 WebChromeClientonJsAlert()onJsConfirm()onJsPrompt() 方法回撥攔截 JS 對話方塊 alert()confirm()prompt() 訊息

但是以上都是要基於 WebView 才可以實現,但是 React Native 並沒有使用 WebView 來實現 JS 和 Android 間的通訊,而是採用 JavaScriptCore 來實現 JS 的解析。

那分析 React Native 的啟動過程,也就是分析 React Native 是如何讓 Android 與 JavaScriptCore 進行關聯的

從 RNTester App Demo 出發

檢視 React Native 專案 RNTester 下的 Android Demo。

文中所有程式碼例項都只會擷取核心程式碼,想檢視完整程式碼請直接檢視原始碼。

public class RNTesterApplication extends Application implements ReactApplication {
  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    public String getJSMainModuleName() {
      // js 入口檔案地址
      return "RNTester/js/RNTesterApp.android";
    }

    @Override
    public @Nullable String getBundleAssetName() {
        // js bundle打包後 放在 asset 目錄下的檔名稱
      return "RNTesterApp.android.bundle";
    }
    
    ...
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }
};
複製程式碼

再看 RNTesterActivity.java 的程式碼

public class RNTesterActivity extends ReactActivity {

    ...

  @Override
  protected String getMainComponentName() {
  // 用來返回要顯示的js端的元件的名稱,這個要和 js 端註冊的 Component 名稱一一對應
    return "RNTesterApp"; 
  }
}
複製程式碼

有點 React Native 的小夥伴知道,上面的 getJSMainModuleNamegetMainComponentName,必須要跟 JS 程式碼保持一直,否則執行程式會找不到對應的 JS 程式碼,但是為什麼要保持一直,這個疑問我們先記下,繼續往後面看。

可以看到 RNTesterActivity 是整合自 ReactActivityReactActivity 是 rn 中頁面顯示的入口,負責頁面的顯示。

進入 ReactActivityonCreate 中發現 ReactActivity 只是一個空殼子,所有的邏輯都交給 ReactActivityDelegate 類實現,這是典型的代理模式,這樣做的好處:

  • 實現和介面分開
  • 可以在 FragmentActivity 也同樣可以使用,不用維護兩套邏輯

檢視 ReactActivityDelegateonCreate 發現最終是呼叫了 loadApp 函式

  protected void loadApp(String appKey) {
    ...
    
    mReactRootView = createRootView();

    // rn 啟動入口
    mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      appKey,
      getLaunchOptions());

    // ReactRootView 作為 ReactActivity 的根 View
    getPlainActivity().setContentView(mReactRootView);
  }
複製程式碼

這個函式主要實現兩個功能:

  • 建立 ReactRootView,並將這個 view 設定為當前 Activity 的根 view
  • 呼叫 ReactRootView 的 startReactApplication 方法,啟動 rn 流

ReactRootView 繼承 FrameLayout,它主要負責 native 端事件(鍵盤事件、touch事件、頁面大小變化等)的監聽並將結果傳遞給 js 端以及負責頁面元素的重新繪製。涉及東西將多,之後會專門進行分析。

通過 startReactApplication 方法名也可知,這裡才是 RN 啟動的入口處。

RN 關鍵類登場

這裡開始 RN 中的關鍵類就會陸續登場了。這裡我們先簡單進行一下相關介紹,讓讀者有個印象。這裡大部分的核心類都是採用面向介面程式設計的思想,括號中是介面對應的實現類。

ReactInstanceManager

rn 的 java 端的控制器,它主要的功能是建立和管理 CatalystInstance,ReactContext 例項並和 ReactActivity 的生命週期保持一致

CatalystInstance (CatalystInstanceImpl)

jsc 橋樑介面類,為 java 和 js 相互通訊提供環境。在 c++ 也有其對應的實現類

JSCJavaScriptExecutor

是 JavaScriptExecutor 的子類,是 js 執行器

JSBundleLoader

bundle.js 檔案載入器,在 rn 中有三種載入方式:1、載入本地檔案;2、載入網路檔案,並將檔案快取;3、載入網路檔案,用於 debug 除錯。前面 Demo Applicatoin 類中的 getBundleAssetName 最終也是會轉化為 JSBundleLoader

ModuleSpec

NativeModule的包裝類,主要是為了實現 module 的懶載入,由於 rn 中 native module 比較多,為了節省成本,rn 中採用時懶載入的策略,只有相應的 module 使用時才進行建立。

JavaScriptModule

介面類,用於 java 呼叫 js 的介面,在 rn 中沒有實現類,具體如何使用後面再介紹

JavaScriptModuleRegistry

JavaScriptModule 的登錄檔。

NativeModuleRegistry

NativeModule 的登錄檔,用於管理 NativeModule 列表。

NativeModule

java 暴露給 js 呼叫的 api 介面,如果想建立自己的 module,需要繼承這個介面。

ReactPackage

元件配置介面類,通過 createNativeModules、createJSModules 和 createViewManagers 等API去建立本地模組,JS 模組及檢視元件等。 ReactPackage 分為 rn 核心的 CoreModulesPackage 和業務方可選的基礎 MainReactPackage 類,其中 CoreModulesPackage 封裝了大部分通訊功能。

ReactContext

整個啟動流程重要建立例項之一就是ReactContext, ReactContext繼承於ContextWrapper,是ReactNative應用的上下文,通過getContext()去獲得,通過它可以訪問ReactNative核心類的實現。

繼續分析原始碼

以上就是 RN 中反覆出現的關鍵類。接著跟著原始碼走

// ReactRootView.java
public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle initialProperties) {
        ...
        
      if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
        // 
        mReactInstanceManager.createReactContextInBackground();
      }
    
      attachToReactInstanceManager();

        ...
  }
複製程式碼

我們這次先來看 attachToReactInstanceManager 函式,看程式碼這個函式會在 ReactContext 建立之後才會呼叫。繼續深入發現最終到了 ReactInstanceManager 的呼叫

// ReactInstanceManager.java

  private void attachRootViewToInstance(
      final ReactRootView rootView,
      CatalystInstance catalystInstance) {
    ...
    // 最終呼叫 AppRegistry.js 的 runApplication 方法
    rootView.invokeJSEntryPoint();
    ...
  }
複製程式碼

發現最終又回到了 ReactRootView 中

// ReactRootView.java
  private void defaultJSEntryPoint() {
        ...
        ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
        if (reactContext == null) {
          return;
        }

        CatalystInstance catalystInstance = reactContext.getCatalystInstance();

        ...

        String jsAppModuleName = getJSModuleName();
        
        // 呼叫 js module
        catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
  }
複製程式碼

在這裡我們發現了一個非常眼熟的類名 AppRegistry.class, 這個不就是在所有入口 js 中都要寫的一行程式碼中的 AppRegistry

AppRegistry.registerComponent('RNTesterApp', () => RNTesterApp);
複製程式碼

所以 defaultJSEntryPoint 函式最終調起了 js 的入口。 再看 defaultJSEntryPoint 函式,發現裡面同時用到了 ReactContextCatalystInstanceReactInstanceManager。所以驗證了他們三者相互之間的重要關係。也說明了 React Native 的啟動重要的就是要建立 ReactContext。至於 Java 是如何呼叫的 JS,我們稍後再分析,現在我們來分析 ReactContext 是如何建立的。

沿著 createReactContextInBackground 的函式流,會發現最終不管是採用載入網路還是本地的 bundle 檔案最終都是會到達 runCreateReactContextOnNewThread 方法中

// ReactInstanceManager.java
  private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
    ...
    mCreateReactContextThread =
        new Thread(
            new Runnable() {
              @Override
              public void run() {
                    ...
                  // 核心部分,建立 ReactContext
                  final ReactApplicationContext reactApplicationContext =
                      createReactContext(
                          initParams.getJsExecutorFactory().create(),
                          initParams.getJsBundleLoader());

                  mCreateReactContextThread = null;
                  ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_START);
                    ...
                  Runnable setupReactContextRunnable =
                      new Runnable() {
                        @Override
                        public void run() {
                          try {
                            // 最終會呼叫 attachRootViewToInstance 即呼叫 defaultJSEntryPoint 啟動 js 入口
                            setupReactContext(reactApplicationContext);
                          } catch (Exception e) {
                            mDevSupportManager.handleException(e);
                          }
                        }
                      };

                    ...
                  UiThreadUtil.runOnUiThread(maybeRecreateReactContextRunnable);
              }
            });
    // 啟動新執行緒
    mCreateReactContextThread.start();
  }
複製程式碼

這個函式主要做了以下幾件事:

  1. 如果已存在 ReactContext,將其進行銷燬
  2. 開啟一個新的執行緒用於建立 ReactContext
  3. ReactContext 建立成功之後,在 UI 執行緒中最終函式流會呼叫 defaultJSEntryPoint 方法來啟動 js 的入口程式

硬骨頭 createReactContext

接下來才是整個 RN 的硬骨頭,只要將其啃下,對 RN 底層的實現也就理解了。因為這裡主要涉及到了 C++ 層面的核心實現,且程式碼實現較長,故不會將程式碼都貼出來,還是希望讀者可以結合著原始碼和這篇文章來分析這塊。

先上createReactContext 函式程式碼

  private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor,
      JSBundleLoader jsBundleLoader) {
        ...
    // 將核心 CoreModulesPackage 和 業務基礎 MainReactPackage 中的 NativeModule 新增到登錄檔中
    NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);

    CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
      .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()) // 初始化 native 執行緒佇列及 js 執行緒佇列
      .setJSExecutor(jsExecutor) // js 執行器
      .setRegistry(nativeModuleRegistry) // 本地提供給 JS 呼叫的 NativeModule 登錄檔
      .setJSBundleLoader(jsBundleLoader) // bundle 資訊類
      .setNativeModuleCallExceptionHandler(exceptionHandler);
        ...
    final CatalystInstance catalystInstance;
        ...
      catalystInstance = catalystInstanceBuilder.build();
        ...
    // 載入 js bundle 檔案
    catalystInstance.runJSBundle();
    
    reactContext.initializeWithInstance(catalystInstance);

    return reactContext;
  }
複製程式碼

這個函式主要做了以下功能:

  1. 根據 ReactPackage 列表生成 Native Module 的登錄檔
  2. 建立 Native 及 JS 的執行緒佇列
  3. 建立 CatalystInstance 例項
  4. 載入 bundle 檔案
  5. 將 UI,Native 及 JS 的執行緒佇列賦予 ReactContext

CatalystInstanceImpl 作為整個 RN 中舉足輕重的角色,來看一下它的建構函式。

  private CatalystInstanceImpl(
      final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry nativeModuleRegistry,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    // 找到於java相對應的c++類並呼叫其構造方法生成物件
    mHybridData = initHybrid();

    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);

    // 呼叫的是 c++ 中對應的實現
    initializeBridge(
      new BridgeCallback(this),
      jsExecutor,
      mReactQueueConfiguration.getJSQueueThread(),
      mNativeModulesQueueThread,
      mNativeModuleRegistry.getJavaModules(this),
      mNativeModuleRegistry.getCxxModules());

    // JSGlobalContextRef 的記憶體地址
    mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());
  }
複製程式碼

建構函式中有兩個需要關注的地方

  1. HybridData 物件在 rn 中會反覆的出現,它的主要作用是找到於 java 相對應的 c++ 類並呼叫其構造方法生成物件,把 new 出來物件的地址放到 java 的 HybridData 物件中
  2. ReactQueueConfigurationSpec:用於配置訊息執行緒,在 rn 中有三個訊息執行緒:UI 執行緒、JS 執行緒、Native 執行緒,其中 native 呼叫 js 的程式碼會 JS 執行緒執行,JS 呼叫 native 的程式碼會在 Native 執行緒中執行

C++ 層核心類登場

在繼續之前需要講解一下 Native 層的核心類

NativeToJsBridge

NativeToJsBridge是Java呼叫JS的橋樑,用來呼叫JS Module,回撥Java

JsToNativeBridge

JsToNativeBridge是JS呼叫Java的橋樑,用來呼叫Java Module

JSCExecutor

native 層的 js 執行器

CatalystInstanceImpl

CatalystInstanceImpl 在 c++ 層對應的實現

Instance

可以看作是 NativeToJsBridge 的代理類,最終的處理都是交由了 NativeToJsBridge

分析 Native 層

從這裡開始將會討論 Native 層對應的操作,上面提到 createReactContext 函式裡進行了 CatalystInstanceImpl 的初始化,而最終的核心其實是在 Native 進行的。看一下 Native 層中 CatalystInstanceImplinitializeBridge 函式

// CatalystInstanceImpl.cpp
void CatalystInstanceImpl::initializeBridge(
    jni::alias_ref<ReactCallback::javaobject> callback,
    // This executor is actually a factory holder.
    JavaScriptExecutorHolder* jseh,
    jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
    jni::alias_ref<JavaMessageQueueThread::javaobject> nativeModulesQueue,
    jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
    jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {

  moduleMessageQueue_ = std::make_shared<JMessageQueueThread>(nativeModulesQueue);

    // 在 native 層將 JavaNativeModule 和 CxxNativeModule 儲存到 登錄檔裡
  moduleRegistry_ = std::make_shared<ModuleRegistry>(
    buildNativeModuleList(
       std::weak_ptr<Instance>(instance_),
       javaModules,
       cxxModules,
       moduleMessageQueue_));

  instance_->initializeBridge(
    folly::make_unique<JInstanceCallback>(
    callback,
    moduleMessageQueue_),
    jseh->getExecutorFactory(),
    folly::make_unique<JMessageQueueThread>(jsQueue),
    moduleRegistry_);
}
複製程式碼

這個函式主要做了以下幾個功能:

  1. 將 java 層的物件分裝成 JavaNativeModule 和 CxxNativeModule 物件,並將生成的物件註冊到 ModuleRegistry 物件中,ModuleRegistry和上面提高的 NativeModuleRegistry 功能相似,是 C++ 端 module 的登錄檔, 並且將 java 的 MessageQueueThread 也傳入每個 NativeModule 中以便之後可以直接呼叫
  2. 跳轉到 Instance.cpp 的 initializeBridge 方法中
// Instance.cpp
void Instance::initializeBridge(
    std::unique_ptr<InstanceCallback> callback,
    std::shared_ptr<JSExecutorFactory> jsef,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<ModuleRegistry> moduleRegistry) {
  callback_ = std::move(callback); // 含有
  moduleRegistry_ = std::move(moduleRegistry);

    // 在 js 執行緒佇列中初始化 NativeToJsBridge
  jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
      // 初始化 NativeToJsBridge
    nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
        jsef.get(), moduleRegistry_, jsQueue, callback_);

    std::lock_guard<std::mutex> lock(m_syncMutex);
    m_syncReady = true;
    m_syncCV.notify_all();
  });
    ...
}
複製程式碼

這個函式主要就是在 js 的執行緒佇列中初始化 NativeToJsBridge,上面也提高了 NativeToJsBridge 類是 Java 呼叫 JS 的橋樑,用來呼叫 JS Module,回撥 Java

// NativeToJsBridge.cpp
NativeToJsBridge::NativeToJsBridge(
    JSExecutorFactory* jsExecutorFactory,
    std::shared_ptr<ModuleRegistry> registry,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<InstanceCallback> callback)
    : m_destroyed(std::make_shared<bool>(false))
    , m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)) // 初始化 JsToNativeBrdige 打通 js 呼叫 native 的橋樑
    , m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)) // js 的執行器
    , m_executorMessageQueueThread(std::move(jsQueue)) // js 執行緒佇列
    {}
複製程式碼

NativeToJsBridge 的建構函式主要是初始化了通訊所需的關鍵類

m_delegateJsToNativeBridge,用於 JS 呼叫 Native 函式,和 NativeToJsBridge 一起作為連線 java 和 js 通訊的橋樑

m_executor 是對應於 JSCExecutor 物件,JSCExecutor 建構函式中對 js 的執行環境進行初始化,並且向 JavaScriptCore 中註冊了幾個 c++ 的方法供 js 端呼叫

到這裡 initializeBridge 整個函式就全部介紹完畢了, 總結一句就是在 ReactInstanceManagercreateReactContext 函式裡初始化 CatalystInstanceImpl 時,會通過 JNI 在 Native 層中初始化 Java 與 JS 通訊所需的關鍵類,將通訊環境搭建完成。

載入 JS Bundle

繼續檢視 createReactContext 的原始碼,看到在 CatalystInstanceImpl 初始化之後會接著呼叫 CatalystInstanceImplrunJSBundle 方法。通過函式的名字可以直接這個方法將會真正的去載入 JS Bundle 檔案。

檢視 runJSBundle 的原始碼發現最終是走到了 JSBundleLoaderloadScript 函式裡。 JSBundleLoader 上面介紹過,是來管理 JS Bundle 的載入方式,不管採用哪種載入方式,最終都是回到CatalystInstanceImpl 進行處理。

private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously);
private native void jniLoadScriptFromFile(String fileName, String sourceURL, boolean loadSynchronously);
private native void jniLoadScriptFromDeltaBundle(String sourceURL, NativeDeltaClient deltaClient, boolean loadSynchronously);
複製程式碼

可以看到 CatalystInstanceImpl 中對應上面提到的三種載入 JS Bundle 的方式,而都是在 Native 層進行處理。

僅需進行程式碼追蹤,會發現三種方法都是在 NativeToJsBridge 中進行了統一處理

// NativeToJsBridge.cpp
void NativeToJsBridge::loadApplication(
    std::unique_ptr<RAMBundleRegistry> bundleRegistry,
    std::unique_ptr<const JSBigString> startupScript,
    std::string startupScriptSourceURL) {
  runOnExecutorQueue( // js 執行緒佇列
      [bundleRegistryWrap=folly::makeMoveWrapper(std::move(bundleRegistry)),
       startupScript=folly::makeMoveWrapper(std::move(startupScript)),
       startupScriptSourceURL=std::move(startupScriptSourceURL)]
        (JSExecutor* executor) mutable {
    auto bundleRegistry = bundleRegistryWrap.move();
    if (bundleRegistry) {
      executor->setBundleRegistry(std::move(bundleRegistry));
    }
    executor->loadApplicationScript(std::move(*startupScript),
                                    std::move(startupScriptSourceURL));
  });
}
複製程式碼

executor 對應的 JSCExecutor 在上面已經說過, 是 JS 的執行器。

// JSCExecutor.cpp
void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {
    ...
    //JavaScriptCore函式,執行js程式碼
    evaluateScript(m_context, jsScript, jsSourceURL);
 
    ...
    flush();
    ...
  }
}
複製程式碼

loadApplicationScript 函式程式碼較多,但是最核心的就是 flush 函式

void JSCExecutor::flush() {
  SystraceSection s("JSCExecutor::flush");

  if (m_flushedQueueJS) {
    callNativeModules(m_flushedQueueJS->callAsFunction({}));
    return;
  }

  // When a native module is called from JS, BatchedBridge.enqueueNativeCall()
  // is invoked.  For that to work, require('BatchedBridge') has to be called,
  // and when that happens, __fbBatchedBridge is set as a side effect.
  auto global = Object::getGlobalObject(m_context);
  auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
  // So here, if __fbBatchedBridge doesn't exist, then we know no native calls
  // have happened, and we were able to determine this without forcing
  // BatchedBridge to be loaded as a side effect.
  if (!batchedBridgeValue.isUndefined()) {
    // If calls were made, we bind to the JS bridge methods, and use them to
    // get the pending queue of native calls.
    bindBridge();
    callNativeModules(m_flushedQueueJS->callAsFunction({}));
  } else if (m_delegate) {
    // If we have a delegate, we need to call it; we pass a null list to
    // callNativeModules, since we know there are no native calls, without
    // calling into JS again.  If no calls were made and there's no delegate,
    // nothing happens, which is correct.
    callNativeModules(Value::makeNull(m_context));
  }
}
複製程式碼

flush 函式主要就是檢查是否已經載入過 js bundle,建立了連線橋樑。如果沒有就呼叫 bindBridge 進行連線。

void JSCExecutor::bindBridge() throw(JSException) {
  SystraceSection s("JSCExecutor::bindBridge");
  std::call_once(m_bindFlag, [this] {
      // 獲取 js 中的 global 物件
    auto global = Object::getGlobalObject(m_context);

      // 獲取儲存在 global 中的 MessageQueue 物件
    auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
    if (batchedBridgeValue.isUndefined()) {
      auto requireBatchedBridge =
          global.getProperty("__fbRequireBatchedBridge");
      if (!requireBatchedBridge.isUndefined()) {
        batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({});
      }
      if (batchedBridgeValue.isUndefined()) {
        throw JSException(
            "Could not get BatchedBridge, make sure your bundle is packaged correctly");
      }
    }

      // 在 native 中儲存 MessageQueue 關鍵的函式物件
    auto batchedBridge = batchedBridgeValue.asObject();
    m_callFunctionReturnFlushedQueueJS =
        batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
    m_invokeCallbackAndReturnFlushedQueueJS =
        batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue")
            .asObject();
    m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
    m_callFunctionReturnResultAndFlushedQueueJS =
        batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue")
            .asObject();
  });
}
複製程式碼

這個函式主要實現以下功能: 1、從 js 執行環境中取出全域性變數 fbBatchedBridge 放到 global 變數中 2、將 global 中某些特定的函式物件對映到 C++ 物件中,這樣我們就可以通過 C++ 物件呼叫 js 的程式碼,假設我們想要呼叫 js 端 fbBatchedBridge 的 flushQueue 方法,在 C++ 中就可以使用 m_flushedQueueJS->callAsFunction() 就可以實現,那麼 fbBatchedBridge 在 js 端到底是個什麼東西那?

檢視 BatchBridge.js 中可以找到 __fbBatchedBridge 的定義

const MessageQueue = require('MessageQueue');

const BatchedBridge = new MessageQueue();

Object.defineProperty(global, '__fbBatchedBridge', {
  configurable: true,
  value: BatchedBridge,
});

module.exports = BatchedBridge;
複製程式碼

可以看出 __fbBatchedBridge 指的就是 JS 中的 MessageQueue 物件,這樣就實現了 Android 端的訊息佇列和 JS 端訊息佇列的連通。

總結

對上面的原始碼流程做了個核心方法的呼叫流程圖

React Native Android 原始碼分析之啟動過程

相關文章