導讀:本文以 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;
}

此處做的事情如下:

  1. 建立 ReactInstanceManagerBuilder 例項。這裡採用建造者模式來構造 ReactInstanceManager 例項,故在此先傳入引數設定構造者;
  2. 把在 ReactNativeHost 中註冊的 packages 都新增到 ReactInstanceManagerBuilder 例項中;
  3. 如果 getJSBundleFile 不為空,則載入對應的檔案,否則載入預設的 jsBundleFile
  4. 呼叫 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。