20分鐘理解React Native For Android原理

MarYZWH 7年前發布 | 7K 次閱讀 ReactNative 移動開發 React Native

前言

  • 文中所有 RN 縮寫指代React Native For Android
  • 分析的 RN 代碼基于
{
 "react": "15.4.1",
 "react-native": "0.39.2"
}
  • 本文主要分析 Java 層實現,對 C++ 和 JS 筆墨較少。
  • 閱讀正文將花費您大約20分鐘。

背景

公司內幾個 APP 已經接入并上線了多個 RN 模塊,后續規劃的定制化需求及性能優化需要我們對 RN 底層原理有更深入的理解。下面通過研讀源代碼來分析和總結下 Android 中的 RN 實現原理。

從示例入手

示例代碼如下:

public class MainActivity extends ReactActivity {  
    @Override
    protected String getMainComponentName() {
        return "RN_Demo";
    }
}

可以發現 RN 容器外層本質也是一個 Activity ,繼承了 ReactActivity ,需要我們覆寫 getMainComponentName() 方法,更改其返回值為組件名。

ReactActivity

接著跟蹤到 ReactActivity 中,類結構如下:

根據上述的結構轉換成 UML 圖如下(后面相關類將直接給出 UML ):

這里使用了 委托模式 將 Activity 的生命周期及事件傳遞委托給 ReactActivityDelegate 的實例對象 mDelegate 進行處理,之所以使用這種形式是為了讓 ReactFragmentActivity 也能復用該處理邏輯。 此外如果你有自定義的委托實現,可以在自己的 Activity 中覆寫 createReactActivityDelegate() 方法。這個方法將在 ReactActivity 的構造函數中調用生成 mDelegate 實例,之后在 onCreate() 方法調用這個委托對象的執行入口,也就是 loadApp() 方法。

ReactActivityDelegate

public class ReactActivityDelegate {
protected void onCreate(Bundle savedInstanceState) { // 彈窗權限判斷邏輯 if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) { // 省略具體實現 } // 組件加載邏輯 if (mMainComponentName != null) { loadApp(mMainComponentName); } // 雙擊判斷工具類 DoubleTapReloadRecognizer ,省略代碼 }

  protected void loadApp(String appKey) {
    // 省略判空代碼

    // 創建 RN 容器根視圖,父類為 FrameLayout
    mReactRootView = createRootView();
    mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      appKey,
      getLaunchOptions());
    // 將 RN 視圖放入 Activity
    getPlainActivity().setContentView(mReactRootView);
  }

}</code></pre>

ReactRootView

因此可以認為所謂的 RN 其實就是一個特殊的“自定義 View ”-- ReactRootView 。

這里的關鍵調用則是 startReactApplication() 方法。下面是該方法需要的三個參數:

形參 類型 功能描述
reactInstanceManager ReactInstanceManager 用來創建及管理 CatalyInstance (提供原生與JS互調環境)的實例,同時連接調試功能,其生命周期與 ReactRootView 所在 Activity 保持一致。
moduleName String 即實參 appKey ,需要保證JS中的 AppRegistry.registerComponent 參數值與 Acitvity 中的 getMainComponentName 返回值一致。
launchOptions Bundle(后續版本可能更改為POJO) 默認為null,如果你需要傳 Props 給 JS 的話,請覆寫 createReactActivityDelegate() 方法,并覆寫 getLaunchOptions() 的返回值即可。

public void startReactApplication(
ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions) { // 確保在 UI 線程執行

// 判斷 ReactContext 是否已初始化,沒有就異步在后臺線程創建
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
  mReactInstanceManager.createReactContextInBackground();
}
// 寬高計算完成后添加布局監聽

}</code></pre>

startReactApplication() 將調用其中的 createReactContextInBackground() 方法,我們下面來看看這個通過 構造者模式 創建的實現類-- XReactInstanceManagerImpl 。

ReactInstanceManager

/**

  • 在后臺線程異步初始化,這個方法只會在 Application 創建時調用一次,
  • 重新加載 JS 時將會調用 recreateReactContextInBackground 方法 */ @Override public void createReactContextInBackground() {
    // 首次執行判斷邏輯 mHasStartedCreatingInitialContext = true; recreateReactContextInBackgroundInner(); }</code></pre>

    可以看到不管是 createReactContextInBackground() 還是 recreateReactContextInBackground() ,都是通過 recreateReactContextInBackgroundInner() 來初始化 ReactContext 的。

    private void recreateReactContextInBackgroundInner() {
    // 確保在UI線程執行

    // 調試模式,從服務器加載 bundle if (mUseDeveloperSupport && mJSMainModuleName != null) {

    // 省略代碼
    return;
    

    }

    // 正式環境,從本地加載 bundle recreateReactContextInBackgroundFromBundleLoader(); }

    private void recreateReactContextInBackgroundFromBundleLoader() { recreateReactContextInBackground(

      new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
      mBundleLoader);
    

    } }</code></pre>

    形參 類型 功能描述
    jsExecutorFactory JavaScriptExecutor.Factory 管理Webkit 的 JavaScriptCore,JS與C++的雙向通信在這里中轉
    jsBundleLoader JSBundleLoader bundle加載器,根據 ReactNativeHost 中的配置決定從哪里加載bundle文件

    XReactInstanceManagerImpl

    private void recreateReactContextInBackground(

    JavaScriptExecutor.Factory jsExecutorFactory,
    JSBundleLoader jsBundleLoader) {
    
    

    // 省略代碼

    / 把兩個參數封裝成ReactContextInitParams對象 ReactContextInitParams initParams =

      new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
    

    if (mReactContextInitAsyncTask == null) {

    // 核心代碼,創建后臺線程,初始化 ReactContext
    mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
    mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
    

    } else {

    // 省略代碼
    

    } }</code></pre>

    ReactContextInitAsyncTask

    private ReactApplicationContext createReactContext(

    JavaScriptExecutor jsExecutor,
    JSBundleLoader jsBundleLoader) {
    

    // 省略代碼

    // 注冊JS層模塊,通過它把所有的 JavaScriptModule 注冊到 CatalystInstance,將 JS 的可調用 API 暴露給 Java。 JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();

    // 包裝 ApplicationContext 為 ReactApplicationContext final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext); // 調試模式 if (mUseDeveloperSupport) { // 調試模式下 ReactApplicationContext 中崩潰信息由 mDevSupportManager 進行攔截處理(紅色背景的錯誤頁) reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager); }

    // 省略代碼

    try {

    // 加載給定的大量核心 ReactPackage,詳細列表請查看后文的表格
    CoreModulesPackage coreModulesPackage =
      new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
    processPackage(
      coreModulesPackage,
      reactContext,
      moduleSpecs,
      reactModuleInfoMap,
      jsModulesBuilder);
    

    } finally {

    // 省略代碼
    

    }

    // 加載 MainPackage 和開發者自定義的 ReactPackage,邏輯同 CoreModulesPackage for (ReactPackage reactPackage : mPackages) {

    // 省略代碼
    
    try {
      processPackage(
        reactPackage,
        reactContext,
        moduleSpecs,
        reactModuleInfoMap,
        jsModulesBuilder);
    } finally {
      // 省略代碼
    }
    

    }

    // 省略代碼

    // 注冊 Java 層模塊,通過它把所有的 NativeModule 注冊到 CatalystInstance ,將 Java 的可調用 API 暴露給 JS。 NativeModuleRegistry nativeModuleRegistry; try {

     nativeModuleRegistry = new NativeModuleRegistry(moduleSpecs, reactModuleInfoMap);
    

    } finally {

    // 省略代碼
    

    }

    // 異常處理器選擇邏輯 NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null

      ? mNativeModuleCallExceptionHandler
      : mDevSupportManager;
    
    

    // 核心邏輯, Builder 模式創建 CatalystInstance 實例 CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()

      .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
      // 將 JS 執行通信類傳給 CatalystInstance
      .setJSExecutor(jsExecutor)
      // 將 Java 模塊映射表傳給 CatalystInstance
      .setRegistry(nativeModuleRegistry)
      // 將 JS 模塊映射表傳給 CatalystInstance
      .setJSModuleRegistry(jsModulesBuilder.build())
      // 將 Bundle 加載工具類傳給 CatalystInstance
      .setJSBundleLoader(jsBundleLoader)
      // 將異常處理器傳給 CatalystInstance
      .setNativeModuleCallExceptionHandler(exceptionHandler);
    
    

    // 省略代碼

    final CatalystInstance catalystInstance; try {

    catalystInstance = catalystInstanceBuilder.build();
    

    } finally {

    // 省略代碼
    

    }

    if (mBridgeIdleDebugListener != null) {

    catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
    

    }

    // 使用 CatalystInstance 實例初始化 ReactContext 部分成員變量 reactContext.initializeWithInstance(catalystInstance);

    // 解析 Bundle 文件 catalystInstance.runJSBundle();

    return reactContext; }</code></pre>

    原生模塊 功能描述
    AndroidInfoModule 獲取Android版本號和本地服務器地址
    AnimationsDebugModule 監聽動畫過渡性能
    DeviceEventManagerModule 事件監聽,比如后退鍵
    ExceptionsManagerModule 異常處理
    HeadlessJsTaskSupportModule 通知部分JS任務執行完成
    SourceCodeModule 傳遞Bundle文件地址
    Timing 在繪制幀率變化時觸發JS定時器
    UIManagerModule 提供JS去創建和更新原生視圖的能力
    DebugComponentOwnershipModule 調試功能:異步請求視圖結構
    JSCHeapCapture 調試功能:獲取堆內存信息
    JSCSamplingProfiler 調試功能:dump工具

    CatalystInstance

    private CatalystInstanceImpl(

    final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
    final JavaScriptExecutor jsExecutor,
    final NativeModuleRegistry registry,
    final JavaScriptModuleRegistry jsModuleRegistry,
    final JSBundleLoader jsBundleLoader,
    NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    

    // 省略代碼

    // Android UI 線程,JS 線程和 NativeModulesQueue 線程 mReactQueueConfiguration = ReactQueueConfigurationImpl.create(

      ReactQueueConfigurationSpec,
      new NativeExceptionHandler());
    
    

    // 調用 C++ 層代碼進行初始化 initializeBridge(

    new BridgeCallback(this),
    jsExecutor,
    mReactQueueConfiguration.getJSQueueThread(),
    mReactQueueConfiguration.getNativeModulesQueueThread(),
    // 將所有 Java Module 傳遞給 C++ 層
    mJavaRegistry.getModuleRegistryHolder(this));
    

    mMainExecutorToken = getMainExecutorToken(); }

@Override public void runJSBundle() { // 省略代碼

mJSBundleHasLoaded = true;
// Bundle 的加載邏輯請參看下文的流程圖
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);

// 省略代碼

}</code></pre>

private native void initializeBridge(ReactCallback callback,
JavaScriptExecutor jsExecutor, MessageQueueThread jsQueue, MessageQueueThread moduleQueue, ModuleRegistryHolder registryHolder);

native void loadScriptFromAssets(AssetManager assetManager, String assetURL);
native void loadScriptFromFile(String fileName, String sourceURL);
native void loadScriptFromOptimizedBundle(String path, String sourceURL, int flags);</code></pre>

此時已經發現Java層的邏輯已經走完,不管是 CatalystInstance 實例的初始化還是 Bundle 的加載邏輯都將由 C++ 層進行處理。

CatalystInstance的創建

CatalystInstanceImpl.cpp 中是空實現,具體實現在 Instance.cpp 中。

void CatalystInstanceImpl::initializeBridge(
// JInstanceCallback 實現類,父類在 cxxreact/Instance.h 中。 std::unique_ptr<InstanceCallback> callback, // 對應 Java 中的 JavaScriptExecutor std::shared_ptr<JSExecutorFactory> jsef, // C++ 的 JMessageQueueThread 。 std::shared_ptr<MessageQueueThread> jsQueue, // C++ 的 JMessageQueueThread 。 std::unique_ptr<MessageQueueThread> nativeQueue, // C++ 的 ModuleRegistryHolder 的 getModuleRegistry() 方法 std::sharedptr<ModuleRegistry> moduleRegistry) { callback = std::move(callback);

jsQueue->runOnQueueSync( [this, &jsef, moduleRegistry, jsQueue, nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable { // 創建 NativeToJsBridge 對象 nativeToJsBridge_ = folly::makeunique<NativeToJsBridge>( jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback);

  std::lock_guard<std::mutex> lock(m_syncMutex);
  m_syncReady = true;
  m_syncCV.notify_all();
});

CHECK(nativeToJsBridge_); }</code></pre>

Bundle 的加載

void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager,  
                                                const std::string& assetURL) {
  const int kAssetsLength = 9;  // strlen("assets://");
  // 獲取 source 路徑名,不包含前綴,默認值為 index.android.bundle
  auto sourceURL = assetURL.substr(kAssetsLength);
  // assetManager 是Java 傳遞的 AssetManager 。
  // extractAssetManager 是JSLoader.cpp 中通過系統動態鏈接庫 android/asset_manager_jni.h的AAssetManager_fromJava 方法來獲取 AAssetManager 對象的。
  auto manager = react::extractAssetManager(assetManager);
  // //通過 JSLoader 對象的 loadScriptFromAssets 方法讀文件,得到大字符串 script(即index.android.bundle文件內容)。
  auto script = react::loadScriptFromAssets(manager, sourceURL);
  // 判斷是否為 Unbundle
  if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
    instance_->loadUnbundle(
      folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),
      std::move(script),
      sourceURL);
    return;
  } else {
    // instance_ 為 ReactCommon 目錄下 Instance.h 中類的實例
    instance_->loadScriptFromString(std::move(script), sourceURL);
  }
}

至此 C++ 層的調用邏輯到此為止,感興趣的同學可以繼續跟蹤進入,或者可以參考文末的資料,我們下面假設底層執行完成,回到 ReactContextInitAsyncTask 的 onPostExecute 方法。

@Override
protected void onPostExecute(Result<ReactApplicationContext> result) {
// 省略代碼

  setupReactContext(result.get());

  // 省略代碼

  // 處理隊列再次初始化 ReactContext 的邏輯代碼

}

private void setupReactContext(ReactApplicationContext reactContext) {
// 省略代碼

// 確保在 UI 線程中執行并且當前 ReactContext 為空
Assertions.assertCondition(mCurrentReactContext == null);

// 確保當前 CatalystInstance 實例非空
CatalystInstance catalystInstance =
    Assertions.assertNotNull(reactContext.getCatalystInstance());
// 初始化所有 Native Module
catalystInstance.initialize();

mDevSupportManager.onNewReactContextCreated(reactContext);
// 添加內存警告的回調
mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);

moveReactContextToCurrentLifecycleState();

for (ReactRootView rootView : mAttachedRootViews) {
  // 核心代碼,給 RootView 添加 view
  attachMeasuredRootViewToInstance(rootView, catalystInstance);
}
// 省略代碼

}

private void attachMeasuredRootViewToInstance(
ReactRootView rootView, CatalystInstance catalystInstance) { // 省略代碼

  // 確保在 UI 線程執行

// 重置視圖內容
rootView.removeAllViews();
rootView.setId(View.NO_ID);

// 設置 RootView
UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
int rootTag = uiManagerModule.addMeasuredRootView(rootView);
rootView.setRootViewTag(rootTag);
@Nullable Bundle launchOptions = rootView.getLaunchOptions();
WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
String jsAppModuleName = rootView.getJSModuleName();

// 獲取 AppRegistry 的代理對象
WritableNativeMap appParams = new WritableNativeMap();
appParams.putDouble("rootTag", rootTag);
appParams.putMap("initialProps", initialProps);
// AppRegistry 是 JS 暴露給 Java 層的 API
catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);

// 省略代碼

}</code></pre>

AppRegistry

public interface AppRegistry extends JavaScriptModule {  
  void runApplication(String appKey, WritableMap appParameters);
  void unmountApplicationComponentAtRootTag(int rootNodeTag);
  void startHeadlessTask(int taskId, String taskKey, WritableMap data);
}

AppRegistry 的 runApplication() 方法成為了加載 index.android.js 的主入口,而所有的 JS 方法調用都會經過 JavaScriptModuleInvocationHandler 。

JavaScriptModuleInvocationHandler

@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
ExecutorToken executorToken = mExecutorToken.get();

// 省略代碼

NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray(); // 調用 C++ 層的 callFunction mCatalystInstance.callFunction( executorToken, mModuleRegistration.getName(), method.getName(), jsArgs ); return null; }</code></pre>

以上就是 RN Android 的執行主流程,如有疏漏,歡迎留言。

參考資料

  1. https://github.com/非死book/react-native
  2. ReactNative Android源碼分析
  3. React Native Android 源碼框架淺析(主流程及 Java 與 JS 雙邊通信)
  4. React Native通訊原理
  5. ReactNativeAndroid源碼分析-Js如何調用Native的代碼

 

來自:https://blog.souche.com/react-native-source-code-analysis/

 

 本文由用戶 MarYZWH 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!