React Native 啟動流程詳細解析

導讀:本文以 react-native-cli 創建的示例工程(安卓部分)為例,分析 React Native 的啟動流程。

工程創建步驟可以參考官網。本文所分析 React Native 版本為 v0.64.2

我們知道上述工程是一個安卓應用,打開 android/ 目錄下源碼文件,首先發現它創建瞭兩個 java 文件:MainApplication.javaMainActivity.java,分別做瞭應用以及主 Activity 的定義。

安卓應用的啟動流程是:在啟動第一個 activity 之前會創建一個全局唯一的 Application 對象。故在此我們先分析 MainApplication

MainApplication

public class MainApplication extends Application implements ReactApplication {
  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;
        }
        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          // 其它對 packages 的操作
          return packages;
        }
        @Override
        protected String getJSMainModuleName() {
          return "index";
        }
  }
  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }
  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
  }

MainApplication 繼承自 Application 類,並且實現瞭 ReactApplication 接口。在其中做的事情有:

1.創建成員變量 ReactNativeHost 的實例,並在創建過程中通過重寫 ReactNativeHost 類方法的方式,註入一些配置,包括:

  1. getUseDeveloperSupport: 配置是否開啟調試
  2. getPackages: 配置要加載的模塊
  3. getJSMainModuleName: 配置 js 模塊的入口文件名

2.在 onCreate 中:

  1. 調用 Soloader 庫。Soloader 是 facebook 推出的一個 so 文件加載庫,它能夠處理 so 文件的依賴在 react-native 中,所有框架相關的 so 文件都是通過SoLoader完成加載的
  2. 通過 ReactInstanceManager 初始化 Flipper。Flipper 是 facebook 推出的用於 debug ios、Android、React Native 應用的工具。

在這裡簡要介紹下 ReactNativeHostReactInstanceManager

ReactNativeHost

ReactNativeHost 是個抽象類,開發者可以重寫其中的方法,其主要的作用是:在 application 中指定一些賦值操作,進而獲取 ReactInstanceManager 的實例。所以可以把 ReactNativeHost 作為將用戶自定義的參數賦值到 ReactInstanceManager 實例的中轉站。核心方法是: getReactInstanceManager,詳細分析見下文。

ReactInstanceManager

該類為核心類,主要負責管理 JS 的加載、維護生命周期、管理 JS 與 C++ 的交互等等。可以把 ReactInstanceManager 理解成 JS 與 C++ 的中轉橋梁。

MainActivity

接著看 MainActivity.java

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

MainActivity 類中僅重寫瞭 getMainComponentName 方法。該類繼承自 ReactActivity,我們再來看其 ReactActivity

public abstract class ReactActivity extends AppCompatActivity
    implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {
  private final ReactActivityDelegate mDelegate;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mDelegate.onCreate(savedInstanceState);
  }

ReactActivity 全權委托給 ReactActivityDelegate 來處理 onCreate 生命周期。來看 ReactActivityDelegateonCreate

protected void onCreate(Bundle savedInstanceState) {
  String mainComponentName = getMainComponentName();
  mReactDelegate =
      new ReactDelegate(
          getPlainActivity(), getReactNativeHost(), mainComponentName, getLaunchOptions()) {
        @Override
        protected ReactRootView createRootView() {
          return ReactActivityDelegate.this.createRootView();
        }
      };
    if (mMainComponentName != null) {
      loadApp(mainComponentName);
    }
  }

這裡首先創建瞭 ReactDelegate 實例。接著來看 loadApp 方法:

protected void loadApp(String appKey) {
  mReactDelegate.loadApp(appKey);
  getPlainActivity().setContentView(mReactDelegate.getReactRootView());
}

由此走到 ReactDelegate 實例的 loadApp 方法:

public void loadApp(String appKey) {
  if (mReactRootView != null) {
    throw new IllegalStateException("Cannot loadApp while app is already running.");
  }
  mReactRootView = createRootView();
  mReactRootView.startReactApplication(
      getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
}

在這裡做瞭三件事:創建 rootView (createRootView)、創建 ReactInstanceManager (getReactInstanceManager)、創建 ReactApplication (startReactApplication)。

createRootView

首先看下什麼是 rootView。

public class ReactRootView extends FrameLayout implements RootView, ReactRoot { /* ... */}

ReactRootView 繼承自 FrameLayout,並且實現瞭 RootViewReactRoot 兩個接口。FrameLayout 是安卓幾大佈局中較為簡單的一個,整個界面被當成一塊空白備用區域,所有元素以左上角對齊堆疊。ReactRootView 繼承自 FrameLayout,表明其也是作為簡單佈局而存在,UI 的繪制渲染都發生在上面。

getReactInstanceManager

ReactInstanceManager 是一個核心類,管理著 JS 的加載、C++ 和 JS 的交互、初始化參數等。最終調用來到 ReactNativeHost 類中的 createReactInstanceManager 方法:

protected ReactInstanceManager createReactInstanceManager() {
  ReactInstanceManagerBuilder builder = /* ... */

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

  String jsBundleFile = getJSBundleFile();
  if (jsBundleFile != null) {
    builder.setJSBundleFile(jsBundleFile);
  } else {
    builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
  }
  
  ReactInstanceManager reactInstanceManager = builder.build();
  return reactInstanceManager;
}

此處做的事情如下:

  • 創建 ReactInstanceManagerBuilder 實例。這裡采用建造者模式來構造 ReactInstanceManager 實例,故在此先傳入參數設定構造者;
  • 把在 ReactNativeHost 中註冊的 packages 都添加到 ReactInstanceManagerBuilder 實例中;
  • 如果 getJSBundleFile 不為空,則加載對應的文件,否則加載默認的 jsBundleFile
  • 調用 builder.build 方法。通過建造者真正構造 ReactInstanceManager 實例

startReactApplication

  public void startReactApplication(/* */) {
    // ...
    try {
      // ...
      mReactInstanceManager.createReactContextInBackground();
    } finally {
      // ...
    }
  }

最終執行到 ReactInstanceManagercreateReactContextInBackground 方法中。最後經過調用鏈:recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()

runCreateReactContextOnNewThread 主要做瞭兩件事:

  1. 創建一個新的線程,並在新線程中通過 createReactContext 創建 ReactContext 上下文;
  2. 通過 setupReactContext 來設置上下文環境,並最終調用到 AppRegistry.js 啟動App。

詳細分析我們放到另一篇文章:React Native startReactApplication 流程梳理。

總結

總結本文,通過 react-native-cli 創建的示例工程(安卓部分)為例,順著兩個類 MainApplicationMainActivity 的執行流程,抓住主幹邏輯,最終梳理出瞭 React Native 從開始啟動至執行用戶 js 文件的過程。可以看到:

MainApplication 的作用主要是傳入用戶的配置,並做 so 庫以及應用 debug 工具的初始化工作;

MainActivity 的作用主要是:

  1. 為應用創建 rootView 佈局容器;
  2. 創建 ReactInstanceManager 核心類,用於後續管理 JS 的加載、C++ 和 JS 的交互、初始化參數等;
  3. 通過 startReactApplication 來創建 ReactContext 上下文,並最終調用到 AppRegistry.js 啟動App。

到此這篇關於React Native 啟動流程簡析的文章就介紹到這瞭,更多相關React Native 啟動內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: