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:
- Creating member variables
ReactNativeHost
And inject some configuration during creation by overriding the ReactNativeHost class methods, including:- GetUseDeveloperSupport: Configures whether debugging is enabled
- GetPackages: Configures modules to load
- GetJSMainModuleName: Configures the entry file name of the JS module
- In the onCreate:
- Call the Soloader library.
Soloader
In React-Native, all framework-related SO files are loaded by SoLoader. SoLoader is a library for loading so files - through
ReactInstanceManager
Initialize Flipper.Flipper
React Native debugs ios, Android, and React Native apps.
- Call the Soloader library.
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:
- create
ReactInstanceManagerBuilder
Instance. The builder pattern is used hereReactInstanceManager
Instance, so pass in the parameter constructor first; - Put in the
ReactNativeHost
Registered in thepackages
Are added to theReactInstanceManagerBuilder
Instance; - if
getJSBundleFile
If not empty, load the corresponding file, otherwise load the defaultjsBundleFile
; - call
builder.build
Methods. True construction by the builderReactInstanceManager
The 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:
- Creates a new thread and passes through it
createReactContext
createReactContext
Context; - through
setupReactContext
To set the context and eventually call toAppRegistry.js
Start 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:
- Creating an application
rootView
Layout container; - create
ReactInstanceManager
Core classes for subsequent management of JS loading, C++ and JS interaction, initialization parameters, etc. - through
startReactApplication
To create aReactContext
Context, and eventually called toAppRegistry.js
Start the App.