ReactNative原始碼篇:啟動流程

蘇策發表於2017-09-28

關於作者

郭孝星,程式設計師,吉他手,主要從事Android平臺基礎架構方面的工作,歡迎交流技術方面的問題,可以去我的Github提issue或者發郵件至guoxiaoxingse@163.com與我交流。

更多文章:github.com/guoxiaoxing…

文章目錄

  • 一 應用的初始化流程
  • 二 應用的啟動流程

更多文章:github.com/guoxiaoxing…

本篇系列文章主要分析ReactNative原始碼,分析ReactNative的啟動流程、渲染原理、通訊機制與執行緒模型等方面內容。

一 應用初始化流程

在分析具體的啟動流程之前,我們先從Demo程式碼入手,對外部的程式碼有個大致的印象,我們才能進一步去了解內部的邏輯。

舉例

1 首先我們會在應用的Application裡做RN的初始化操作。

  //ReactNativeHost:持有ReactInstanceManager例項,做一些初始化操作。
  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    //SoLoader:載入C++底層庫,準備解析JS。
    SoLoader.init(this, /* native exopackage */ false);
  }
}複製程式碼

2 頁面繼承ReactActivity,ReactActivity作為JS頁面的容器。

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        //返回元件名
        return "standard_project";
    }
}複製程式碼

3 有了ReactActivity作為容器,我們就可以用JS開發頁面了。

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

//Component用來做UI渲染,生命週期控制,事件分發與回撥。
export default class standard_project extends Component {
  //render函式返回UI的介面結構(JSX編寫,編譯完成後最終會變成JS程式碼)
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>
        <Text style={styles.instructions}> 
          Double tap R on your keyboard to reload,{'\n'}
          Shake or press menu button for dev menu
        </Text>
      </View>
    );
  }
}

//建立CSS樣式
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

//註冊元件名,JS與Java格子各自維護了一個登錄檔
AppRegistry.registerComponent('standard_project', () => standard_project);複製程式碼

可以看到我們在Application裡實現了ReactApplication介面,該介面要求建立一個ReactNativeHost物件,該物件持有ReactInstanceManager例項,做一些初始化操作。

public interface ReactApplication {

  /**
   * Get the default {@link ReactNativeHost} for this app.
   */
  ReactNativeHost getReactNativeHost();
}複製程式碼

在建立ReactNativeHost物件時,重寫了裡面的介個方法,這些方法提供一些初始化資訊,具體說來:


//是否開啟dev模式,dev模式下會有一些除錯工具,例如紅盒
public abstract boolean getUseDeveloperSupport();

//返回app需要的ReactPackage,這些ReactPackage裡包含了執行時需要用到的NativeModule
//JavaScriptModule以及ViewManager
protected abstract List<ReactPackage> getPackages();複製程式碼

ReactNativeHost主要的工作就是建立了ReactInstanceManager,它將一些資訊傳遞給了ReactInstanceManager。

public abstract class ReactNativeHost {

      protected ReactInstanceManager createReactInstanceManager() {
        ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
          //應用上下文
          .setApplication(mApplication)
          //JSMainModuleP相當於應用首頁的js Bundle,可以傳遞url從伺服器拉取js Bundle
          //當然這個只在dev模式下可以使用
          .setJSMainModulePath(getJSMainModuleName())
          //是否開啟dev模式
          .setUseDeveloperSupport(getUseDeveloperSupport())
          //紅盒的回撥
          .setRedBoxHandler(getRedBoxHandler())
          //自定義UI實現機制,這個我們一般用不到
          .setUIImplementationProvider(getUIImplementationProvider())
          .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);

        //新增ReactPackage
        for (ReactPackage reactPackage : getPackages()) {
          builder.addPackage(reactPackage);
        }

        //獲取js Bundle的載入路徑
        String jsBundleFile = getJSBundleFile();
        if (jsBundleFile != null) {
          builder.setJSBundleFile(jsBundleFile);
        } else {
          builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
        }
        return builder.build();
      }
}複製程式碼

二 應用啟動流程

一句話概括啟動流程:先是應用終端啟動並建立應用上下文,應用上下文啟動JS Runtime,進行佈局,再由應用終端進行渲染,最後將渲染的View新增到ReactRootView上,最終呈現在使用者面前。

RN應用的啟動流程圖如下所示:

ReactNative原始碼篇:啟動流程

詳細流程:

1 在程式啟動的時候,也就是ReContextactActivity的onCreate()函式中,我們會去建立一個ReactInstanceManagerImpl物件

2 ReactRootView作為整個RN應用的根檢視,通過呼叫ReactRootView.startReactApplication()方法啟動RN應用。

3 RN應用頁面渲染前,需要先建立ReactContext的建立流程在,非同步任務ReactContextInitAsyncTask負責來完成這個任務。

4 ReactContextInitAsyncTask在後臺ReactContextInitAsyncTask.doInBackground()執行ReactContext的建立,建立ReactContext的過程中,會依據ReactPackage建立JavaScriptModuleRegistry與
NativeModuleRegistry登錄檔以及它們的管理類CatalystInstanceImpl,同時建立JS、Native與UI執行緒佇列,並最終呼叫CatalystInstanceImpl.runJSBundle()去非同步
載入JS Bundle檔案。

5 後臺任務執行完成後,在ReactContextInitAsyncTask.onPostExecute()會呼叫ReactInstanceManager.setupReactContext()設定建立好的ReactContext,並將
ReactRootView載入進來,並呼叫RN應用的JS入口APPRegistry來啟動應用。

6 JS層找到已經註冊的對應的啟動元件,執行renderApplication()來渲染整個應用。複製程式碼

ReactNative原始碼篇:啟動流程

好,我們先從ReactActivity入手。

ReactActivity繼承於Activity,並實現了它的生命週期方法。ReactActivity自己並沒有做什麼事情,所有的功能都由它的委託類ReactActivityDelegate來完成。

如下所示:

ReactNative原始碼篇:啟動流程

所以我們主要來關注ReactActivityDelegate的實現。我們先來看看ReactActivityDelegate的onCreate()方法。

2.1 ReactActivityDelegate.onCreate(Bundle savedInstanceState)

public class ReactActivityDelegate {

  protected void onCreate(Bundle savedInstanceState) {
    boolean needsOverlayPermission = false;
    //開發模式判斷以及許可權檢查
    if (getReactNativeHost().getUseDeveloperSupport() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      // Get permission to show redbox in dev builds.
      if (!Settings.canDrawOverlays(getContext())) {
        needsOverlayPermission = true;
        Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getContext().getPackageName()));
        FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE);
        Toast.makeText(getContext(), REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show();
        ((Activity) getContext()).startActivityForResult(serviceIntent, REQUEST_OVERLAY_PERMISSION_CODE);
      }
    }

    //mMainComponentName就是上面ReactActivity.getMainComponentName()返回的元件名
    if (mMainComponentName != null && !needsOverlayPermission) {
        //載入app頁面
      loadApp(mMainComponentName);
    }
    mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
  }

  protected void loadApp(String appKey) {
    if (mReactRootView != null) {
      throw new IllegalStateException("Cannot loadApp while app is already running.");
    }
    //建立ReactRootView作為根檢視,它本質上是一個FrameLayout
    mReactRootView = createRootView();
    //啟動RN應用
    mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(),
      appKey,
      getLaunchOptions());
    //Activity的setContentView()方法  
    getPlainActivity().setContentView(mReactRootView);
  }
}複製程式碼

可以發現ReactActivityDelegate在建立時主要做了以下事情:

1 建立ReactRootView作為應用的容器,它本質上是一個FrameLayout。
2 呼叫ReactRootView.startReactApplication()進一步執行應用啟動流程。
3 呼叫Activity.setContentView()將建立的ReactRootView作為ReactActivity的content view。複製程式碼

尅看出RN真正核心的地方就在於ReactRootView,它就是一個View,你可以像用其他UI元件那樣把它用在Android應用的任何地方。好,我們進一步去ReactRootView看啟動流程。

2.2 ReactRootView.startReactApplication( ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions)

public class ReactRootView extends SizeMonitoringFrameLayout implements RootView {

  /**
   * Schedule rendering of the react component rendered by the JS application from the given JS
   * module (@{param moduleName}) using provided {@param reactInstanceManager} to attach to the
   * JS context of that manager. Extra parameter {@param launchOptions} can be used to pass initial
   * properties for the react component.
   */
  public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle launchOptions) {
    UiThreadUtil.assertOnUiThread();

    // TODO(6788889): Use POJO instead of bundle here, apparently we can't just use WritableMap
    // here as it may be deallocated in native after passing via JNI bridge, but we want to reuse
    // it in the case of re-creating the catalyst instance
    Assertions.assertCondition(
        mReactInstanceManager == null,
        "This root view has already been attached to a catalyst instance manager");

    mReactInstanceManager = reactInstanceManager;
    mJSModuleName = moduleName;
    mLaunchOptions = launchOptions;

    //建立RN應用上下文
    if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
      mReactInstanceManager.createReactContextInBackground();
    }

    // We need to wait for the initial onMeasure, if this view has not yet been measured, we set which
    // will make this view startReactApplication itself to instance manager once onMeasure is called.
    if (mWasMeasured) {
      attachToReactInstanceManager();
    }
  }

}複製程式碼

這個方法用來建立應用的上下文,RN應用的上下文用ReactContext來描述。

我們來看看這個函式的3個引數:

···
ReactInstanceManager reactInstanceManager:管理React例項。
String moduleName:模組的名字,對應ReactActivity.getMainComponentName()與AppRegistry.registerComponent()。
Bundle launchOptions:Bundle型別的資料,如果我們不繼承ReactActivity而是自己實現頁面容器,可以通過這個引數在startActivity()時傳遞引數到JS層。
···

我們可以看到,ReactRootView.startReactApplication()方法裡最終會呼叫ReactInstanceManager.createReactContextInBackground()來建立RN應用的上下文。

2.3 ReactInstanceManager.createReactContextInBackground()

public class ReactInstanceManager {

 /**
   * Trigger react context initialization asynchronously in a background async task. This enables
   * applications to pre-load the application JS, and execute global code before
   * {@link ReactRootView} is available and measured. This should only be called the first time the
   * application is set up, which is enforced to keep developers from accidentally creating their
   * application multiple times without realizing it.
   *
   * Called from UI thread.
   */
  public void createReactContextInBackground() {
    Assertions.assertCondition(
        !mHasStartedCreatingInitialContext,
        "createReactContextInBackground should only be called when creating the react " +
            "application for the first time. When reloading JS, e.g. from a new file, explicitly" +
            "use recreateReactContextInBackground");

    mHasStartedCreatingInitialContext = true;
    //進一步呼叫recreateReactContextInBackgroundInner()
    recreateReactContextInBackgroundInner();
  }

  /**
   * Recreate the react application and context. This should be called if configuration has
   * changed or the developer has requested the app to be reloaded. It should only be called after
   * an initial call to createReactContextInBackground.
   *
   * Called from UI thread.
   */
  public void recreateReactContextInBackground() {
    Assertions.assertCondition(
        mHasStartedCreatingInitialContext,
        "recreateReactContextInBackground should only be called after the initial " +
            "createReactContextInBackground call.");
    recreateReactContextInBackgroundInner();
  }

  private void recreateReactContextInBackgroundInner() {
    UiThreadUtil.assertOnUiThread();

    //開發模式,實現線上更新Bundle,晃動彈出除錯選單等功能,這一部分屬於除錯功能流程。
    if (mUseDeveloperSupport && mJSMainModuleName != null) {
      final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();

      // If remote JS debugging is enabled, load from dev server.
      //判斷是否處於開發模式,如果處於開發模式,則從Dev Server中獲取JSBundle,如果不是則從檔案中獲取。
      if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
          !devSettings.isRemoteJSDebugEnabled()) {
        // If there is a up-to-date bundle downloaded from server,
        // with remote JS debugging disabled, always use that.
        onJSBundleLoadedFromServer();
      } else if (mBundleLoader == null) {
        mDevSupportManager.handleReloadJS();
      } 

      else {
        mDevSupportManager.isPackagerRunning(
            new PackagerStatusCallback() {
              @Override
              public void onPackagerStatusFetched(final boolean packagerIsRunning) {
                UiThreadUtil.runOnUiThread(
                    new Runnable() {
                      @Override
                      public void run() {
                        if (packagerIsRunning) {
                          mDevSupportManager.handleReloadJS();
                        } else {
                          // If dev server is down, disable the remote JS debugging.
                          devSettings.setRemoteJSDebugEnabled(false);
                          recreateReactContextInBackgroundFromBundleLoader();
                        }
                      }
                    });
              }
            });
      }
      return;
    }

    //線上模式
    recreateReactContextInBackgroundFromBundleLoader();
  }

  private void recreateReactContextInBackgroundFromBundleLoader() {
    //mJSCConfig可以在ReactNativeHost建立ReactInstanceManager時進行配置。mJSCConfig會通過JSCJavaScriptExecutor的
    //Native方法HybridData initHybrid(ReadableNativeArray jscConfig)傳遞到C++層。
    recreateReactContextInBackground(
        new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
        mBundleLoader);
  }

  private void recreateReactContextInBackground(
      JavaScriptExecutor.Factory jsExecutorFactory,
      JSBundleLoader jsBundleLoader) {
    UiThreadUtil.assertOnUiThread();

    ReactContextInitParams initParams =
        new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
    if (mReactContextInitAsyncTask == null) {
      //初始化一個非同步任務,建立ReactApplicationContext
      // No background task to create react context is currently running, create and execute one.
      mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
      mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
    } else {
      //建立ReactContext的後臺任務已經開啟,快取initParams在佇列中等待重新建立ReactContext
      // Background task is currently running, queue up most recent init params to recreate context
      // once task completes.
      mPendingReactContextInitParams = initParams;
    }
  }

}複製程式碼

整個程式碼的呼叫鏈,最終開啟非同步任務ReactContextInitAsyncTask來建立上下文ReactApplicationContext。

ReactInstanceManager.createReactContextInBackground()
->ReactInstanceManager.recreateReactContextInBackground()
->ReactInstanceManager.recreateReactContextInBackgroundInner()
->ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()
->ReactInstanceManager.recreateReactContextInBackground(JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader)
->ReactContextInitAsyncTask

該方法啟動了一個ReactContextInitAsyncTask的非同步任務去執行的建立。

2.4 ReactInstanceManager.ReactContextInitAsyncTask.doInBackground(ReactContextInitParams... params)

public class ReactInstanceManager {

 /*
   * Task class responsible for (re)creating react context in the background. These tasks can only
   * be executing one at time, see {@link #recreateReactContextInBackground()}.
   */
  private final class ReactContextInitAsyncTask extends
      AsyncTask<ReactContextInitParams, Void, Result<ReactApplicationContext>> {

    @Override
    protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
      // TODO(t11687218): Look over all threading
      // Default priority is Process.THREAD_PRIORITY_BACKGROUND which means we'll be put in a cgroup
      // that only has access to a small fraction of CPU time. The priority will be reset after
      // this task finishes: https://android.googlesource.com/platform/frameworks/base/+/d630f105e8bc0021541aacb4dc6498a49048ecea/core/java/android/os/AsyncTask.java#256
      Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);

      Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
      try {
        //利用getJsExecutorFactory建立jsExecutor,並傳遞到C++層。
        JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
        //非同步執行createReactContext()方法,建立ReactContext
        return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
      } catch (Exception e) {
        // Pass exception to onPostExecute() so it can be handled on the main thread
        return Result.of(e);
      }
    }
}複製程式碼

ReactContextInitAsyncTask的doInBackground()方法裡呼叫ReactInstanceManager.createReactContext()最終執行了ReactApplicationContext的建立。
我們重點來看看傳入ReactInstanceManager.createReactContext()的2個引數:

JSCJavaScriptExecutor jsExecutor:JSCJavaScriptExecutor繼承於JavaScriptExecutor,當該類被載入時,它會自動去載入"reactnativejnifb.so"庫,並會呼叫Native方
法initHybrid()初始化C++層RN與JSC通訊的框架。

JSBundleLoader jsBundleLoader:快取了JSBundle的資訊,封裝了上層載入JSBundle的相關介面,CatalystInstance通過其簡介呼叫ReactBridge去載入JS檔案,不同的場景會建立
不同的載入器,具體可以檢視類JSBundleLoader。複製程式碼

這兩個引數是ReactInstanceManager.recreateReactContextInBackground()建立ReactContextInitAsyncTask傳遞進來的,有兩個地方呼叫了ReactInstanceManager.recreateReactContextInBackground()
方法,

接下來呼叫ReactInstanceManager.createReactContext(),真正開始建立ReactContext。

2.5 ReactInstanceManager.createReactContext( JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader)

public class ReactInstanceManager {

/**
   * @return instance of {@link ReactContext} configured a {@link CatalystInstance} set
   */
  private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor,
      JSBundleLoader jsBundleLoader) {
    FLog.i(ReactConstants.TAG, "Creating react context.");
    ReactMarker.logMarker(CREATE_REACT_CONTEXT_START);
    //ReactApplicationContext是ReactContext的包裝類。
    final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
    //建立JavaModule登錄檔Builder,用來建立JavaModule登錄檔,JavaModule登錄檔將所有的JavaModule註冊到CatalystInstance中。
    NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder(
      reactContext,
      this,
      mLazyNativeModulesEnabled);
    //建立JavaScriptModule登錄檔Builder,用來建立JavaScriptModule登錄檔,JavaScriptModule登錄檔將所有的JavaScriptModule註冊到CatalystInstance中。
    JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
    if (mUseDeveloperSupport) {
      //如果處於開發模式,則設定NativeModuleCallExceptionHandler,將錯誤交由DevSupportManager處理(彈出紅框,提示錯誤)。
      reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
    }

    ReactMarker.logMarker(PROCESS_PACKAGES_START);
    Systrace.beginSection(
        TRACE_TAG_REACT_JAVA_BRIDGE,
        "createAndProcessCoreModulesPackage");
    try {
      //建立CoreModulesPackage例項,CoreModulesPackage裡面封裝了RN Framework核心功能,包括:通訊、除錯等。
      CoreModulesPackage coreModulesPackage =
        new CoreModulesPackage(
          this,
          mBackBtnHandler,
          mUIImplementationProvider,
          mLazyViewManagersEnabled);
      //呼叫processPackage(0處理CoreModulesPackage,處理的過程就是把各自的Module新增到對應的登錄檔中。
      processPackage(coreModulesPackage, nativeModuleRegistryBuilder, jsModulesBuilder);
    } finally {
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    // TODO(6818138): Solve use-case of native/js modules overriding
    for (ReactPackage reactPackage : mPackages) {
      Systrace.beginSection(
          TRACE_TAG_REACT_JAVA_BRIDGE,
          "createAndProcessCustomReactPackage");
      try {
        //迴圈處理我們在Application裡注入的ReactPackage,處理的過程就是把各自的Module新增到對應的登錄檔中。
        processPackage(reactPackage, nativeModuleRegistryBuilder, jsModulesBuilder);
      } finally {
        Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
      }
    }
    ReactMarker.logMarker(PROCESS_PACKAGES_END);

    ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START);
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
    NativeModuleRegistry nativeModuleRegistry;
    try {
       //生成Java Module登錄檔
       nativeModuleRegistry = nativeModuleRegistryBuilder.build();
    } finally {
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
      ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
    }

    //檢視外部是否設定NativeModuleCallExceptionHandler,它是在ReactInstanceManagerBuilder構建ReactInstanceManager是傳遞進來的
    //如果設定了則使用外部NativeModuleCallExceptionHandler,如果沒有設定則使用DevSupportManager。
    NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
        ? mNativeModuleCallExceptionHandler
        : mDevSupportManager;
    //jsExecutor、nativeModuleRegistry、nativeModuleRegistry等各種引數處理好之後,開始構建CatalystInstanceImpl例項。
    CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
        .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
        .setJSExecutor(jsExecutor)
        .setRegistry(nativeModuleRegistry)
        //生成JS Module登錄檔
        .setJSModuleRegistry(jsModulesBuilder.build())
        .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);
    }

    if (mBridgeIdleDebugListener != null) {
      catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
    }
    if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JSC_CALLS)) {
      //呼叫CatalystInstanceImpl的Native方法把Java Registry轉換為Json,再由C++層傳送到JS層。
      catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
    }

    //關聯ReacContext與CatalystInstance
    reactContext.initializeWithInstance(catalystInstance);
    //通過CatalystInstance開始載入JS Bundle
    catalystInstance.runJSBundle();

    return reactContext;
  }
}複製程式碼

這個方法有點長,它主要做了以下事情:

1 建立JavaModule登錄檔與JavaScriptModule登錄檔,這兩張表最後都交由CatalystInstance管理。
3 處理ReactPackage,將JavaModule與JavaScriptModule放進各自對應的登錄檔裡。
3 通過上面jsExecutor、nativeModuleRegistry、jsModulesRegistry、jsBundleLoader、exceptionHandler等引數建立CatalystInstance例項。
4 關聯ReactContext與CatalystInstance,並將JS Bundle載入進來,等待ReactContextInitAsyncTask結束以後呼叫JS入口渲染頁面。複製程式碼

該函式的最後呼叫CatalystInstance.runJSBundle()去載入JS Bundle,該載入過程的函式呼叫鏈如下所示:


CatalystInstance.runJSBundle() -> JSBundleLoader.loadScript() -> CatalystInstance.jniLoadScriptFromAssets()/jniLoadScriptFromFile()
-> CatalystInstance::jniLoadScriptFromAssets()/jniLoadScriptFromFile() -> Instance::loadScriptFromString()/loadScriptFromFile()
-> NativeToJsBridge::loadApplication() -> JSCExecutor::loadApplicationScript()複製程式碼

最終由C++中的JSCExecutor.cpp完成了JS Bundle的載入,核心邏輯都在JSCExecutor.cpp中,這一塊的內容我們後續的文章在詳細分析,我們先來看看CatalystInstanceImpl的建立流程。

2.6 CatalystInstanceImpl.CatalystInstanceImpl( final ReactQueueConfigurationSpec ReactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry registry, final JavaScriptModuleRegistry jsModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler)

public class CatalystInstanceImpl implements CatalystInstance {

private CatalystInstanceImpl(
      final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry registry,
      final JavaScriptModuleRegistry jsModuleRegistry,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge.");

    //Native方法,用來建立JNI相關狀態,並返回mHybridData。
    mHybridData = initHybrid();

    //RN中的三個執行緒:Native Modules Thread、JS Thread、UI Thread,都是通過Handler來管理的。
    mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
        ReactQueueConfigurationSpec,
        new NativeExceptionHandler());
    mBridgeIdleListeners = new CopyOnWriteArrayList<>();
    mJavaRegistry = registry;
    mJSModuleRegistry = jsModuleRegistry;
    mJSBundleLoader = jsBundleLoader;
    mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
    mTraceListener = new JSProfilerTraceListener(this);

    FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
    //Native方法,呼叫initializeBridge()方法,並建立BridgeCallback例項,初始化Bridge。
    initializeBridge(
      new BridgeCallback(this),
      jsExecutor,
      mReactQueueConfiguration.getJSQueueThread(),
      mReactQueueConfiguration.getNativeModulesQueueThread(),
      mJavaRegistry.getJavaModules(this),
      mJavaRegistry.getCxxModules());
    FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
    mMainExecutorToken = getMainExecutorToken();
  }

  //在C++層初始化通訊橋ReactBridge
  private native void initializeBridge(
      ReactCallback callback,
      JavaScriptExecutor jsExecutor,
      MessageQueueThread jsQueue,
      MessageQueueThread moduleQueue,
      Collection<JavaModuleWrapper> javaModules,
      Collection<ModuleHolder> cxxModules);
}複製程式碼

從CatalystInstanceImpl的構建過程可以看出,CatalystInstanceImpl是個封裝管理類,封裝了各種登錄檔,以及初始化JNI,我們來看看最後初始化Bridge傳入的6個引數:

ReactCallback callback:CatalystInstanceImpl的靜態內部類ReactCallback,負責介面回撥。
JavaScriptExecutor jsExecutor:JS執行器,將JS的呼叫傳遞給C++層。
MessageQueueThread jsQueue.getJSQueueThread():JS執行緒,通過mReactQueueConfiguration.getJSQueueThread()獲得,mReactQueueConfiguration通過ReactQueueConfigurationSpec.createDefault()建立。
MessageQueueThread moduleQueue:Native執行緒,通過mReactQueueConfiguration.getNativeModulesQueueThread()獲得,mReactQueueConfiguration通過ReactQueueConfigurationSpec.createDefault()建立。
Collection<JavaModuleWrapper> javaModules:java modules,來源於mJavaRegistry.getJavaModules(this)。
Collection<ModuleHolder> cxxModules):c++ modules,來源於mJavaRegistry.getCxxModules()。複製程式碼

CatalystInstanceImpl被建立以後,便進行JS的載入。從上面第5步:ReactInstanceManager.createReactContext()方法可以知道,該函式會調
用CatalystInstanceImpl.runJSBundle()來載入JS Bundle。我們開看一下它的實現。

2.7 CatalystInstanceImpl.runJSBundle()

···java
public class CatalystInstanceImpl{

@Override
public void runJSBundle() {
Assertions.assertCondition(!mJSBundleHasLoaded, "JS bundle was already loaded!");
mJSBundleHasLoaded = true;

// incrementPendingJSCalls();
//呼叫載入器載入JS Bundle,不同情況下載入器各不相同。
mJSBundleLoader.loadScript(CatalystInstanceImpl.this);

synchronized (mJSCallsPendingInitLock) {
  // Loading the bundle is queued on the JS thread, but may not have
  // run yet.  It's safe to set this here, though, since any work it
  // gates will be queued on the JS thread behind the load.
  mAcceptCalls = true;

  for (PendingJSCall call : mJSCallsPendingInit) {
    jniCallJSFunction(call.mExecutorToken, call.mModule, call.mMethod, call.mArguments);
  }
  mJSCallsPendingInit.clear();
}

// This is registered after JS starts since it makes a JS call
Systrace.registerListener(mTraceListener);複製程式碼

}
}
···

CatalystInstanceImpl.runJSBundle()會呼叫JSBundleLoader去載入JS Bundle,由於不同的情況可能會有不同的JSBundleLoader,我們假設用的是第一種:


public abstract class JSBundleLoader {

  /**
   * This loader is recommended one for release version of your app. In that case local JS executor
   * should be used. JS bundle will be read from assets in native code to save on passing large
   * strings from java to native memory.
   */
  public static JSBundleLoader createAssetLoader(
      final Context context,
      final String assetUrl) {
    return new JSBundleLoader() {
      @Override
      public String loadScript(CatalystInstanceImpl instance) {
        instance.loadScriptFromAssets(context.getAssets(), assetUrl);
        return assetUrl;
      }
    };
  }

}複製程式碼

可以看出,它會繼續呼叫CatalystInstanceImpl.loadScriptFromAssets()方法去載入JS Bundle,該方法的實現如下所示:

2.8 CatalystInstanceImpl.loadScriptFromAssets(AssetManager assetManager, String assetURL)

public class CatalystInstanceImpl {

  /* package */ void loadScriptFromAssets(AssetManager assetManager, String assetURL) {
    mSourceURL = assetURL;
    jniLoadScriptFromAssets(assetManager, assetURL);
  }

  private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL);

}複製程式碼

CatalystInstanceImpl.java最終還是呼叫C++層的CatalystInstanceImpl.cpp去載入JS Bundle,我們去C++層看一下實現。

ReactNative原始碼篇:啟動流程

可以看出該方法最終呼叫Native方法jniLoadScriptFromAssets去載入JS Bundle,該方法的實現如下所示:

2.9 CatalystInstanceImpl::jniLoadScriptFromAssets(jni::alias_ref assetManager, const std::string& assetURL)

CatalystInstanceImpl.cpp


void CatalystInstanceImpl::jniLoadScriptFromAssets(
    jni::alias_ref<JAssetManager::javaobject> assetManager,
    const std::string& assetURL) {
  const int kAssetsLength = 9;  // strlen("assets://");
  //獲取source js Bundle的路徑名,這裡預設的就是index.android.bundle
  auto sourceURL = assetURL.substr(kAssetsLength);
  //assetManager是Java層傳遞過來的AssetManager,呼叫JSLoade.cpo裡的extractAssetManager()方法,extractAssetManager()再
  //呼叫android/asset_manager_jni.h裡的AAssetManager_fromJava()方法獲取AAssetManager物件。
  auto manager = react::extractAssetManager(assetManager);
  //呼叫JSLoader.cpp的loadScriptFromAssets()方法讀取JS Bundle裡的內容。
  auto script = react::loadScriptFromAssets(manager, sourceURL);
  //判斷是不是unbundle命令打包,build.gradle預設裡是bundle打包方式。
  if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
    instance_->loadUnbundle(
      folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),
      std::move(script),
      sourceURL);
    return;
  } else {
    //bundle命令打包走次流程,instance_是Instan.h中類的例項。
    instance_->loadScriptFromString(std::move(script), sourceURL);
  }
}複製程式碼

接著會呼叫Instance.cpp的loadScriptFromString()方法去解析JS Bundle裡的內容。

2.10 Instance::loadScriptFromString(std::unique_ptr string, std::string sourceURL)

Instance.cpp

void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
                                    std::string sourceURL) {
  callback_->incrementPendingJSCalls();
  SystraceSection s("reactbridge_xplat_loadScriptFromString",
                    "sourceURL", sourceURL);
  //nativeToJsBridge_也是在Instance::initializeBridget()方法裡初始化的,具體實現在NativeToJsBridge.cpp裡。
  nativeToJsBridge_->loadApplication(nullptr, std::move(string), std::move(sourceURL));
}複製程式碼

loadScriptFromString()進一步呼叫NativeToJsBridge.cpp的loadApplication()方法,它的實現如下所示:

2.11 NativeToJsBridge::loadApplication(std::unique_ptr unbundle, std::unique_ptr startupScript, std::string startupScriptSourceURL)

NativeToJsBridge.cpp

void NativeToJsBridge::loadApplication(
    std::unique_ptr<JSModulesUnbundle> unbundle,
    std::unique_ptr<const JSBigString> startupScript,
    std::string startupScriptSourceURL) {

  //獲取一個MessageQueueThread,探後線上程中執行一個Task。
  runOnExecutorQueue(
      m_mainExecutorToken,
      [unbundleWrap=folly::makeMoveWrapper(std::move(unbundle)),
       startupScript=folly::makeMoveWrapper(std::move(startupScript)),
       startupScriptSourceURL=std::move(startupScriptSourceURL)]
        (JSExecutor* executor) mutable {

    auto unbundle = unbundleWrap.move();
    if (unbundle) {
      executor->setJSModulesUnbundle(std::move(unbundle));
    }

    //executor從runOnExecutorQueue()返回的map中取得,與OnLoad中的JSCJavaScriptExecutorHolder對應,也與
    //Java中的JSCJavaScriptExecutor對應。它的例項在JSExecutor.cpp中實現。
    executor->loadApplicationScript(std::move(*startupScript),
                                    std::move(startupScriptSourceURL));
  });
}

關於unbundle命令

<unbundle命令,使用方式和bundle命令完全相同。unbundle命令是在bundle命令的基礎上增加了一項功能,除了生成整合JS檔案index.android.bundle外,還會
生成各個單獨的未整合JS檔案(但會被優化),全部放在js-modules目錄下,同時會生成一個名為UNBUNDLE的標識檔案,一併放在其中。UNBUNDLE標識檔案的前4個位元組
固定為0xFB0BD1E5,用於載入前的校驗。複製程式碼

我們先來看看這個函式的3個引數:

std::unique_ptr<JSModulesUnbundle> unbundle:空指標,因為我們用的bundle方式打包。
std::unique_ptr<const JSBigString> startupScript:bundle的檔案內容。
std::string startupScriptSourceURL:bundle的檔名。複製程式碼

該函式進一步呼叫JSExecutor.cpp的loadApplicationScript()方法。

2.12 JSCExecutor::loadApplicationScript(std::unique_ptr script, std::string sourceURL)

到了這個方法,就是去真正載入JS檔案了。

JSExecutor.cpp


void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) {
  SystraceSection s("JSCExecutor::loadApplicationScript",
                    "sourceURL", sourceURL);

  ReactMarker::logMarker("RUN_JS_BUNDLE_START");
  String jsSourceURL(m_context, sourceURL.c_str());

  // TODO t15069155: reduce the number of overrides here
#ifdef WITH_FBJSCEXTENSIONS
  if (auto fileStr = dynamic_cast<const JSBigFileString *>(script.get())) {
    JSLoadSourceStatus jsStatus;
    auto bcSourceCode = JSCreateSourceCodeFromFile(fileStr->fd(), jsSourceURL, nullptr, &jsStatus);

    switch (jsStatus) {
    case JSLoadSourceIsCompiled:
      if (!bcSourceCode) {
        throw std::runtime_error("Unexpected error opening compiled bundle");
      }

      //使用Webkit JSC去解釋執行JS
      evaluateSourceCode(m_context, bcSourceCode, jsSourceURL);
      //繫結bridge,核心就是通過getGlobalObject()將JS與C++通過Webkit jSC實現繫結
      bindBridge();
      flush();

      ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
      ReactMarker::logMarker("RUN_JS_BUNDLE_END");
      return;

    case JSLoadSourceErrorVersionMismatch:
      throw RecoverableError(explainLoadSourceStatus(jsStatus));

    case JSLoadSourceErrorOnRead:
    case JSLoadSourceIsNotCompiled:
      // Not bytecode, fall through.
      break;
    }
  }
#elif defined(__APPLE__)
  BundleHeader header;
  memcpy(&header, script->c_str(), std::min(script->size(), sizeof(BundleHeader)));
  auto scriptTag = parseTypeFromHeader(header);

  if (scriptTag == ScriptTag::BCBundle) {
    using file_ptr = std::unique_ptr<FILE, decltype(&fclose)>;
    file_ptr source(fopen(sourceURL.c_str(), "r"), fclose);
    int sourceFD = fileno(source.get());

    JSValueRef jsError;
    JSValueRef result = JSC_JSEvaluateBytecodeBundle(m_context, NULL, sourceFD, jsSourceURL, &jsError);
    if (result == nullptr) {
      formatAndThrowJSException(m_context, jsError, jsSourceURL);
    }
  } else
#endif
  {
    #ifdef WITH_FBSYSTRACE
    fbsystrace_begin_section(
      TRACE_TAG_REACT_CXX_BRIDGE,
      "JSCExecutor::loadApplicationScript-createExpectingAscii");
    #endif

    ReactMarker::logMarker("loadApplicationScript_startStringConvert");
    String jsScript = jsStringFromBigString(m_context, *script);
    ReactMarker::logMarker("loadApplicationScript_endStringConvert");

    #ifdef WITH_FBSYSTRACE
    fbsystrace_end_section(TRACE_TAG_REACT_CXX_BRIDGE);
    #endif

    evaluateScript(m_context, jsScript, jsSourceURL);
  }

  bindBridge();
  flush();

  ReactMarker::logMarker("CREATE_REACT_CONTEXT_END");
  ReactMarker::logMarker("RUN_JS_BUNDLE_END");
}複製程式碼

evaluateScript()方法呼叫Webkit jSC開始解析執行JS,並呼叫bindBridge()繫結bridge,我們這裡主要分析的啟動流程,先不分析JS渲染過程,先看看Bridge繫結
流程,bindBridge()的實現如下所示:

JSExecutor.cpp

void JSCExecutor::bindBridge() throw(JSException) {
  SystraceSection s("JSCExecutor::bindBridge");
  if (!m_delegate || !m_delegate->getModuleRegistry()) {
    return;
  }
  auto global = Object::getGlobalObject(m_context);
  auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
  if (batchedBridgeValue.isUndefined()) {
    throwJSExecutionException("Could not get BatchedBridge, make sure your bundle is packaged correctly");
  }

  auto batchedBridge = batchedBridgeValue.asObject();
  //callFunctionReturnFlushedQueue這些都是MessageQueue.js層裡的方法
  m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
  m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
  //通過Webkit JSC獲取MessageQueue.js的flushedQueue。
  m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
  m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
}

void JSCExecutor::flush() {
  SystraceSection s("JSCExecutor::flush");
  if (!m_delegate) {
    // do nothing
  } else if (!m_delegate->getModuleRegistry()) {
    callNativeModules(Value::makeNull(m_context));
  } else {
    // If this is failing, chances are you have provided a delegate with a
    // module registry, but haven't loaded the JS which enables native function
    // queueing.  Add BatchedBridge.js to your bundle, pass a nullptr delegate,
    // or make delegate->getModuleRegistry() return nullptr.
    CHECK(m_flushedQueueJS) << "Attempting to use native methods without loading BatchedBridge.js";
    //m_flushedQueueJS->callAsFunction({})等於呼叫MessageQueue.js的flushedQUeue()方法,即把JS層相關通訊資料通過flushedQUeue()
    //返回給callNativeModules
    callNativeModules(m_flushedQueueJS->callAsFunction({}));
  }
}

void JSCExecutor::callNativeModules(Value&& value) {
  SystraceSection s("JSCExecutor::callNativeModules");
  try {
    //把JS層相關通訊資料轉換為JSON格式
    auto calls = value.toJSONString();
    //m_delegate為JsToNativeBridge物件。
    m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
  } catch (...) {
    std::string message = "Error in callNativeModules()";
    try {
      message += ":" + value.toString().str();
    } catch (...) {
      // ignored
    }
    std::throw_with_nested(std::runtime_error(message));
  }
}複製程式碼

m_flushedQueueJS支線的是MessageQueue.js的flushedQueue()方法,此時JS已經被載入到佇列中,等待Java層來驅動它。載入完JS後
ReactContextInitAsyncTask的後臺任務執行完成,進入到非同步任務的onPostExecute()方法繼續

JS Bundle載入並解析完成後,ReactContextInitAsyncTask的後臺任務完成,進入onPostExecute()方法,我們繼續跟進它的實現。

ReactNative原始碼篇:啟動流程

當ReactContext被建立以後,變回繼續執行ReactContextInitAsyncTask.onPostExecute()方法。

2.13 ReactInstanceManager.ReactContextInitAsyncTask.onPostExecute(Result result)

public class ReactInstanceManager {

 /*
   * Task class responsible for (re)creating react context in the background. These tasks can only
   * be executing one at time, see {@link #recreateReactContextInBackground()}.
   */
  private final class ReactContextInitAsyncTask extends
      AsyncTask<ReactContextInitParams, Void, Result<ReactApplicationContext>> {

    @Override
    protected void onPostExecute(Result<ReactApplicationContext> result) {
      try {
        //設定ReacContext
        setupReactContext(result.get());
      } catch (Exception e) {
        mDevSupportManager.handleException(e);
      } finally {
        mReactContextInitAsyncTask = null;
      }

      // Handle enqueued request to re-initialize react context.
      if (mPendingReactContextInitParams != null) {
        recreateReactContextInBackground(
            mPendingReactContextInitParams.getJsExecutorFactory(),
            mPendingReactContextInitParams.getJsBundleLoader());
        mPendingReactContextInitParams = null;
      }
    }
}複製程式碼

doInBackground()做完事情之後,onPostExecute()會去呼叫ReactInstanceManager.setupReactContext(),它的實現如下所示:

2.14 ReactInstanceManager.setupReactContext(ReactApplicationContext reactContext)

public class ReactInstanceManager {

  private void setupReactContext(ReactApplicationContext reactContext) {
    ReactMarker.logMarker(SETUP_REACT_CONTEXT_START);
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext");
    UiThreadUtil.assertOnUiThread();
    Assertions.assertCondition(mCurrentReactContext == null);
    mCurrentReactContext = Assertions.assertNotNull(reactContext);
    CatalystInstance catalystInstance =
        Assertions.assertNotNull(reactContext.getCatalystInstance());

    //執行Native Java module的初始化
    catalystInstance.initialize();
    //重置DevSupportManager的ReactContext
    mDevSupportManager.onNewReactContextCreated(reactContext);
    //記憶體狀態回撥設定
    mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
    //復位生命週期
    moveReactContextToCurrentLifecycleState();

    //mAttachedRootViews儲存的是ReactRootView
    for (ReactRootView rootView : mAttachedRootViews) {
      attachMeasuredRootViewToInstance(rootView, catalystInstance);
    }

    ReactInstanceEventListener[] listeners =
      new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
    listeners = mReactInstanceEventListeners.toArray(listeners);

    for (ReactInstanceEventListener listener : listeners) {
      listener.onReactContextInitialized(reactContext);
    }
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(SETUP_REACT_CONTEXT_END);
  }


  private void attachMeasuredRootViewToInstance(
      ReactRootView rootView,
      CatalystInstance catalystInstance) {
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachMeasuredRootViewToInstance");
    UiThreadUtil.assertOnUiThread();

    //移除並重置所有頁面UI元素
    // Reset view content as it's going to be populated by the application content from JS
    rootView.removeAllViews();
    rootView.setId(View.NO_ID);

    //將ReactRootView作為根佈局
    UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
    int rootTag = uiManagerModule.addMeasuredRootView(rootView);
    //設定相關
    rootView.setRootViewTag(rootTag);

    //包裝啟動引數launchOptions與模組名jsAppModuleName
    @Nullable Bundle lau∂nchOptions與模組名 = rootView.getLaunchOptions();
    WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
    String jsAppModuleName = rootView.getJSModuleName();

    WritableNativeMap appParams = new WritableNativeMap();
    appParams.putDouble("rootTag", rootTag);
    appParams.putMap("initialProps", initialProps);

    //啟動流程入口:由Java層呼叫啟動
    catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
    rootView.onAttachedToReactInstance();
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
  }
}複製程式碼

ReactNative原始碼篇:啟動流程

ReactInstanceManager.attachMeasuredRootViewToInstance()最終進入了RN應用的啟動流程入口,呼叫catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams),
AppRegistry.class是JS層暴露給Java層的介面方法。它的真正實現在AppRegistry.js裡,AppRegistry.js是執行所有RN應用的JS層入口,我們來看看它的實現:

2.15 AppRegistry.runApplication(appKey: string, appParameters: any)

AppRegistry.js


  //上面程式碼最終呼叫的就是這個函式
  runApplication(appKey: string, appParameters: any): void {
    const msg =
      'Running application "' + appKey + '" with appParams: ' +
      JSON.stringify(appParameters) + '. ' +
      '__DEV__ === ' + String(__DEV__) +
      ', development-level warning are ' + (__DEV__ ? 'ON' : 'OFF') +
      ', performance optimizations are ' + (__DEV__ ? 'OFF' : 'ON');
    infoLog(msg);
    BugReporting.addSource('AppRegistry.runApplication' + runCount++, () => msg);
    invariant(
      runnables[appKey] && runnables[appKey].run,
      'Application ' + appKey + ' has not been registered.\n\n' +
      'Hint: This error often happens when you\'re running the packager ' +
      '(local dev server) from a wrong folder. For example you have ' +
      'multiple apps and the packager is still running for the app you ' +
      'were working on before.\nIf this is the case, simply kill the old ' +
      'packager instance (e.g. close the packager terminal window) ' +
      'and start the packager in the correct app folder (e.g. cd into app ' +
      'folder and run \'npm start\').\n\n' +
      'This error can also happen due to a require() error during ' +
      'initialization or failure to call AppRegistry.registerComponent.\n\n'
    );
    runnables[appKey].run(appParameters);
  },複製程式碼

到這裡就會去呼叫JS進行元件渲染,再通過Java層的UIManagerModule將JS元件轉換為Android元件,最終顯示在ReactRootView上。

相關文章