React Native Android 源碼分析之啓動過程

前言

閱讀以前請先查看準備工做javascript

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

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

對於 Android 調用 JS 代碼的方法有如下兩種:android

  • 經過 WebViewloadUrl() 函數
  • 經過 WebViewevaluateJavascript() 函數

對於 JS 調用 Android 代碼的方法有如下三種:c++

  • 經過 WebViewaddJavascriptInterface()進行對象映射
  • 經過 WebViewClientshouldOverrideUrlLoading() 方法回調攔截 url
  • 經過 WebChromeClientonJsAlert()onJsConfirm()onJsPrompt() 方法回調攔截 JS 對話框 alert()confirm()prompt() 消息

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

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

從 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 的代碼app

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 中有三種加載方式:一、加載本地文件;二、加載網絡文件,並將文件緩存;三、加載網絡文件,用於 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();
  });
}
複製代碼

這個函數主要實現如下功能: 一、從 js 執行環境中取出全局變量 fbBatchedBridge 放到 global 變量中 二、將 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 端消息隊列的連通。

總結

對上面的源碼流程作了個核心方法的調用流程圖

相關文章
相關標籤/搜索