This article uses the React-Native CLI project (Android) as an example to analyze the startup process of React Native.

For details about how to create the project, see the official website. The React Native version analyzed in this paper is V0.64.2.

Java (mainapplication. Java) and mainactivity. Java (mainactivity. Java), which define the application and MainActivity respectively.

The startup process for android apps is that a globally unique Application object is created before the first activity is started. Therefore, we first analyze MainApplication

MainApplication

public class MainApplication extends Application implements ReactApplication {
  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
        public boolean getUseDeveloperSupport(a) {
          return BuildConfig.DEBUG;
        }
        @Override
        protected List<ReactPackage> getPackages(a) {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          Other operations on Packages
          return packages;
        }
        @Override
        protected String getJSMainModuleName(a) {
          return "index"; }}@Override
  public ReactNativeHost getReactNativeHost(a) {
    return mReactNativeHost;
  }
  @Override
  public void onCreate(a) {
    super.onCreate();
    SoLoader.init(this./* native exopackage */ false);
    initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
  }
Copy the code

MainApplication inherits from the Application class and implements the ReactApplication interface. Among the things to do are:

  1. Creating member variablesReactNativeHostAnd inject some configuration during creation by overriding the ReactNativeHost class methods, including:
    1. GetUseDeveloperSupport: Configures whether debugging is enabled
    2. GetPackages: Configures modules to load
    3. GetJSMainModuleName: Configures the entry file name of the JS module
  2. In the onCreate:
    1. Call the Soloader library.SoloaderIn React-Native, all framework-related SO files are loaded by SoLoader. SoLoader is a library for loading so files
    2. throughReactInstanceManagerInitialize Flipper.FlipperReact Native debugs ios, Android, and React Native apps.

ReactNativeHost and ReactInstanceManager are briefly introduced here

ReactNativeHost

ReactNativeHost is an abstract class that allows developers to override its methods. The main purpose of ReactNativeHost is to specify some assignment operations in the Application and then get an instance of ReactInstanceManager. So you can use ReactNativeHost as a hub for assigning user-defined parameters to the ReactInstanceManager instance. The core method is: getReactInstanceManager, detailed analysis see below.

ReactInstanceManager

The core class is responsible for managing JS loading, maintaining the life cycle, managing JS interaction with C++, and so on. ReactInstanceManager can be understood as a bridge between JS and C++.

MainActivity

Mainactivity.java:

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

Only the getMainComponentName method is overridden in the MainActivity class. This class inherits from ReactActivity, so let’s look at its 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);
  }
Copy the code

The ReactActivity gives carte Blanche to the ReactActivityDelegate to handle the onCreate lifecycle. Look at the onCreate in the ReactActivityDelegate.

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

The ReactDelegate instance is created first. Then look at the loadApp method:

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

This leads to the loadApp method of the ReactDelegate instance:

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);
}
Copy the code

Three things are done here: Create a rootView (createRootView), a ReactInstanceManager (getReactInstanceManager), and a ReactApplication (startReactApplication).

createRootView

Let’s first look at what a rootView is.

public class ReactRootView extends FrameLayout implements RootView.ReactRoot { / *... * /}
Copy the code

ReactRootView inherits FrameLayout and implements RootView and ReactRoot interfaces. FrameLayout is one of the simpler android layouts where the entire interface is treated as a blank spare area, with elements stacked aligned in the top left corner. ReactRootView inherits from FrameLayout, indicating that it also exists as a simple layout where UI rendering takes place.

getReactInstanceManager

ReactInstanceManager is a core class that manages JS loading, C++ and JS interaction, initialization parameters, and more. Final call to ReactNativeHost createReactInstanceManager method in the class:

protected ReactInstanceManager createReactInstanceManager(a) {
  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;
}
Copy the code

Here’s what you do:

  1. createReactInstanceManagerBuilderInstance. The builder pattern is used hereReactInstanceManagerInstance, so pass in the parameter constructor first;
  2. Put in theReactNativeHostRegistered in thepackagesAre added to theReactInstanceManagerBuilderInstance;
  3. ifgetJSBundleFileIf not empty, load the corresponding file, otherwise load the defaultjsBundleFile;
  4. callbuilder.buildMethods. True construction by the builderReactInstanceManagerThe instance

startReactApplication

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

Ultimately perform to ReactInstanceManager createReactContextInBackground method. Finally through the call chain: recreateReactContextInBackgroundInner() -> recreateReactContextInBackgroundFromBundleLoader() -> recreateReactContextInBackground() -> runCreateReactContextOnNewThread()

RunCreateReactContextOnNewThread mainly do two things:

  1. Creates a new thread and passes through itcreateReactContextcreateReactContextContext;
  2. throughsetupReactContextTo set the context and eventually call toAppRegistry.jsStart the App.

React Native startReactApplication

conclusion

To summarize this article, take the example project created by react-Native CLI (Android part) as an example, follow the execution flow of two classes MainApplication and MainActivity, seize the trunk logic, Finally, the process of React Native from startup to user JS file execution is sorted out. You can see:

The main function of MainApplication is to pass in the configuration of the user, and do the initialization work of so library and application debug tool;

MainActivity provides the following functions:

  1. Creating an applicationrootViewLayout container;
  2. createReactInstanceManagerCore classes for subsequent management of JS loading, C++ and JS interaction, initialization parameters, etc.
  3. throughstartReactApplicationTo create aReactContextContext, and eventually called toAppRegistry.jsStart the App.