【Android】ReactNative Android端啓動流程

當RN項目創建完成後,打開android目錄,可以看到在原生代碼中會生成 MainActivity和 MainApplication 兩個Java類。很明顯沒,MainActivity 即爲原生層應用程序的入口文件。MainApplication作爲整體應用程序的初始化入口文件。我們先來看 MainActivity.java 文件:
public class MainActivity extends ReactActivity {
/**
* 返回在rn index.js 註冊的名稱,
* 即 AppRegistry.registerComponent()傳入的名稱,用來渲染組件。
*/
@Override
protected String getMainComponentName() {
return “example”;
}
}
可以看到MainActivity繼承 ReactActivity 並實現 getMainComponentName 方法,返回與 AppRegistry.registerComponent 的 appKey 相同名稱即可。繼續來看 MainApplication.java 文件:
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() {
  return Arrays.<ReactPackage>asList(
      new MainReactPackage()
  );
}

@Override
protected String getJSMainModuleName() {
  return "index";
}

};

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

@Override
public void onCreate() {
super.onCreate();
// 加載C++層渲染代碼
SoLoader.init(this,false);
}
}
MainApplication 中主要完成了三件事:

  • (1)實現 ReactApplication 接口,重寫 getReactNativeHost 方法,返回ReactNativeHost實例。
  • (2)定義並初始化 ReactNativeHost,實現 getUseDeveloperSupport、getPackages、getJSMainModuleName 方法,完成初始化設置。
  • (3)在 onCreate 生命週期方法中,調用SoLoader的init方法,啓動C++層邏輯代碼的初始化加載。

瞭解完兩個文件,我們先從 MainActivity 開始分析。

MainActivity
MainActivity 繼承 ReactActivity 類,重寫了getMainComponentName 方法,並且方法的返回值需要和我們在JS端的值保持一致。

通過上面我們分析的 AppRegistry.js 中的 runApplication 方法發現:如果 getMainComponentName 中返回的名稱與 RN 層 AppRegistry.registerComponent 註冊名稱不一致,會出現 Application XXX appKey has not been registered 異常。跟進 ReactActivity 類,看下核心代碼:
package com.facebook.react;

/**

  • Base Activity for React Native applications.
    */
    public abstract class ReactActivity extends Activity implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {

private final ReactActivityDelegate mDelegate;

protected ReactActivity() {
mDelegate = createReactActivityDelegate();
}

/**

  • 返回從JavaScript註冊的主要組件的名稱,用於組件的渲染。
    */
    protected @Nullable String getMainComponentName() {
    return null;
    }

/**

  • 在構造時調用,如果有自定義委託實現,則覆蓋.
    */
    protected ReactActivityDelegate createReactActivityDelegate() {
    return new ReactActivityDelegate(this, getMainComponentName());
    }

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDelegate.onCreate(savedInstanceState);
}

… 中間省略生命週期、返回事件、權限申請函數

/**

  • 獲取 ReactNativeHost 實例
    */
    protected final ReactNativeHost getReactNativeHost() {
    return mDelegate.getReactNativeHost();
    }

/**

  • 獲取 ReactInstanceManager 實例
    */
    protected final ReactInstanceManager getReactInstanceManager() {
    return mDelegate.getReactInstanceManager();
    }

/**

  • 加載 JSBundle
    */
    protected final void loadApp(String appKey) {
    mDelegate.loadApp(appKey);
    }
    }
    ReactActivity類中主要完成這幾件事:
  • (1)繼承 Activity,實現 DefaultHardwareBackBtnHandler、PermissionAwareActivity 兩個接口。重寫其中的返回事件,及請求權限的方法。
  • (2)構造函數中調用 createReactActivityDelegate 方法,傳入this、和 getMainComponentName 方法返回值,創建 ReactActivityDelegate實例。
  • (3)重寫 Activity 生命週期方法,調用 delegate 實例的對應生命週期方法。
  • (4)定義獲取 ReactNativeHost、ReactInstanceManager 實例方法。
  • (5)定義 loadApp方法。
    可以看出ReactActivity採用了委託的方式,將所有的行爲都交給ReactActivityDelegate去處理,降低耦合提升可擴展性,接下來我們瞎看ReactActivityDelegate是如何定義的
    ReactActivityDelegate
    package com.facebook.react;

/**

  • Delegate class for {@link ReactActivity} and {@link ReactFragmentActivity}. You can subclass this
  • to provide custom implementations for e.g. {@link #getReactNativeHost()}, if your Application
  • class doesn’t implement {@link ReactApplication}.
    */
    public class ReactActivityDelegate {

private final @Nullable Activity mActivity;
private final @Nullable FragmentActivity mFragmentActivity;
private final @Nullable String mMainComponentName;

private @Nullable ReactRootView mReactRootView;
private @Nullable DoubleTapReloadRecognizer mDoubleTapReloadRecognizer;

public ReactActivityDelegate(Activity activity, @Nullable String mainComponentName) {
mActivity = activity;
mMainComponentName = mainComponentName;
mFragmentActivity = null;
}

public ReactActivityDelegate(
FragmentActivity fragmentActivity,
@Nullable String mainComponentName) {
mFragmentActivity = fragmentActivity;
mMainComponentName = mainComponentName;
mActivity = null;
}

protected @Nullable Bundle getLaunchOptions() {
return null;
}

protected ReactRootView createRootView() {
return new ReactRootView(getContext());
}

/**

  • Get the {@link ReactNativeHost} used by this app. By default, assumes
  • {@link Activity#getApplication()} is an instance of {@link ReactApplication} and calls
  • {@link ReactApplication#getReactNativeHost()}. Override this method if your application class
  • does not implement {@code ReactApplication} or you simply have a different mechanism for
  • storing a {@code ReactNativeHost}, e.g. as a static field somewhere.
    */
    protected ReactNativeHost getReactNativeHost() {
    return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
    }

public ReactInstanceManager getReactInstanceManager() {
return getReactNativeHost().getReactInstanceManager();
}

protected void onCreate(Bundle savedInstanceState) {
if (mMainComponentName != null) {
loadApp(mMainComponentName);
}
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
}

protected void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException(“Cannot loadApp while app is already running.”);
}
mReactRootView = createRootView();
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(),
appKey,
getLaunchOptions());
getPlainActivity().setContentView(mReactRootView);
}

… 中間省略生命週期、返回事件、權限請求的方法

private Context getContext() {
if (mActivity != null) {
return mActivity;
}
return Assertions.assertNotNull(mFragmentActivity);
}

private Activity getPlainActivity() {
return ((Activity) getContext());
}
}
ReactActivityDelegate 類中主要完成這幾件事:

  • (1)定義 getReactNativeHost、getReactInstanceManager 方法,獲取對應實例。可以看到此處調用的是MainApplication中定義的ReactNativeHost實例。這裏也就明白,爲什麼MainApplication需要實現ReactApplication接口,並重寫 getReactNativeHost方法了。如果不實現 ReactApplication,可以把ReactNativeHost定義成全局靜態常量即可。
  • (2)定義生命週期方法、返回事件方法、權限請求方法。並在方法中調用 ReactInstanceManager 中對應的方法。
  • (3)定義 createRootView 方法,在該方法中,通過 new ReactRootView,創建 ReactRootView 實例。
  • (4)在 onCreate 生命週期中,判斷 mMainComponentName,如果不爲 null,則執行 loadApp 方法。所以我們重點來看 loadApp 方法。
  • (5)在 loadApp 方法中主要做了三件事:
    •      1. 創建 RootView
      
    •      2. 調用 RootView 實例的 startReactApplication 方法,將 ReactInstanceManager 實例、appKey、啓動時初始化參數作爲參數傳遞過去
      
    •      3. 將 ReactRootView 設置爲 MainActivity 佈局視圖
      

從 ReactActivityDelegate 方法中,我們瞭解到很多方法都交給了 ReactInstanceManager 實例去處理,ReactInstanceManager實例是通過 MainApplication 類中初始化的 ReactNativeHost 實例獲取,在分析 ReactInstanceManager 之前,先跟進 ReactNativeHost 源碼看一看:
ReactNativeHost
package com.facebook.react;

/**

  • 包含 ReactInstanceManager 實例的簡單類, 在 MainApplication 中定義 或 定義成靜態字段使用。
    */
    public abstract class ReactNativeHost {

private final Application mApplication;
private @Nullable ReactInstanceManager mReactInstanceManager;

protected ReactNativeHost(Application application) {
mApplication = application;
}

/**

  • 獲取 或 創建 ReactInstanceManager 實例
    */
    public ReactInstanceManager getReactInstanceManager() {
    if (mReactInstanceManager == null) {
    mReactInstanceManager = createReactInstanceManager();
    }
    return mReactInstanceManager;
    }

/**

  • 獲取此持有者是否包含{@link ReactInstanceManager}實例
    */
    public boolean hasInstance() {
    return mReactInstanceManager != null;
    }

/**

  • 銷燬當前實例並釋放對其的內部引用,允許它進行GCed
    */
    public void clear() {
    if (mReactInstanceManager != null) {
    mReactInstanceManager.destroy();
    mReactInstanceManager = null;
    }
    }

/**

  • 創建 ReactInstanceManager 實例
    */
    protected ReactInstanceManager createReactInstanceManager() {
    ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
    .setApplication(mApplication)
    .setJSMainModulePath(getJSMainModuleName())
    .setUseDeveloperSupport(getUseDeveloperSupport())
    .setRedBoxHandler(getRedBoxHandler())
    .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
    .setJSIModulesPackage(getJSIModulePackage())
    .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
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;

}

protected final Application getApplication() {
return mApplication;
}

protected @Nullable
JSIModulePackage getJSIModulePackage() {
return null;
}

/**

  • 返回 JSBundle 主模塊的名稱。 確定用於獲取JS包的URL來自打包服務器。
  • 它僅在啓用dev支持時使用。
  • 這是創建 ReactInstanceManager 後要執行的第一個文件。
  • 默認 index”
    */
    protected String getJSMainModuleName() {
    return “index”;
    }

/**

  • 返回捆綁文件的自定義路徑。 這是在應該加載bundle的情況下使用的自定義路徑。例如“文件://sdcard/myapp_cache/index.android.bundle”
  • 默認情況下,它是從指定路徑的Android assets目錄下加載
    */
    protected @Nullable String getJSBundleFile() {
    return null;
    }

/**

  • 返回資產中包的名稱。 如果爲null,則不指定文件路徑捆綁。
  • 該方法只能與 getUseDeveloperSupport 一起使用並且將會總是嘗試從打包服務器加載JS包。
  • 默認爲 index.android.bundle
    */
    protected @Nullable String getBundleAssetName() {
    return “index.android.bundle”;
    }

/**

  • 返回是否啓用dev模式
    */
    public abstract boolean getUseDeveloperSupport();

/**

  • 返回應用程序使用的 ReactPackage 列表,至少返回 MainReactPackage。
  • 如果您的應用使用除默認視圖或模塊之外的其他視圖或模塊,您需要在此處添加更多套餐。
    */
    protected abstract List getPackages();
    }
    ReactRootView

ReactInstanceManager

CatalystInstance

JSBundleLoader

CatalystInstanceImpl

CatalustInstanceImpl.cpp

NativeToJsBridge.cpp

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章