ReactNative[0.60.5] start process of source code parsing (Android)

It's nearly two years since contacting RN development, during which 5 or 6 apps have been developed, and the version of react native is also in rapid iteration. Today, start again, analyze the app startup process from the source code, and the resolution is based on Rn version 0.60.5.

Before the start

Before starting the analysis, create a new empty project named RnDemo. Select 0.60.5 for RN version. By viewing the Android part of the directory structure of the project, and files will be automatically generated for us. Our analysis starts from these two files.

Java part, start to upload

1. First, take a look at the MainApplication file, inherit the Application and implement the ReactApplication interface, mainly for the initialization operation of writing RN.

public class MainApplication extends Application implements ReactApplication {
  // Implement the ReactApplication interface, create the ReactNativeHost member variable, hold the ReactInstanceManager instance, and do some initialization operations.
  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
  // Whether to enable dev debugging, and some debugging tools, such as red box, sometimes we see error reports
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    // Return the react package required by the app and add the modules to be loaded. This is where we need to add the third-party package when we add the dependent package in the project
    protected List<ReactPackage> getPackages() {
      List<ReactPackage> packages = new PackageList(this).getPackages();
      // Packages that cannot be autolinked yet can be added manually here, for example:
      // packages.add(new MyReactNativePackage());
      return packages;

    protected String getJSMainModuleName() {
      return "index";

  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;

  public void onCreate() {
   //SoLoader: load the C + + underlying library and prepare to parse JS.
    SoLoader.init(this, /* native exopackage */ false);

2. Next, take a look at the MainActivity file, which inherits from reactactivity, which is the real container of JS page

public class MainActivity extends ReactActivity {

     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
    protected String getMainComponentName() {
       //Return the component name, which is consistent with the js entry registration name
        return "RnDemo";
In the corresponding js module registration name:
AppRegistry.registerComponent("RnDemo", () => App);

3. Go on, take a look at ReactActivity,

public abstract class ReactActivity extends AppCompatActivity
    implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {

  private final ReactActivityDelegate mDelegate;

  protected ReactActivity() {
    mDelegate = createReactActivityDelegate();
   * Called at construction time, override if you have a custom delegate implementation.
  protected ReactActivityDelegate createReactActivityDelegate() {
    return new ReactActivityDelegate(this, getMainComponentName());
  protected void onCreate(Bundle savedInstanceState) {
  protected final ReactNativeHost getReactNativeHost() {
    return mDelegate.getReactNativeHost();

  protected final ReactInstanceManager getReactInstanceManager() {
    return mDelegate.getReactInstanceManager();

  protected final void loadApp(String appKey) {

As you can see from the above code, the real implementation is in the ReactActivityDelegate class.
4. Continue, let's focus on the content of ReactActivityDelegate

public class ReactActivityDelegate {
  protected void onCreate(Bundle savedInstanceState) {
//mMainComponentName is the component name returned by ReactActivity.getMainComponentName() above
    String mainComponentName = getMainComponentName();
    if (mainComponentName != null) {
// Load app page
// Double click to judge tool class
    mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();

  protected void loadApp(String appKey) {
// Non empty judgement
    if (mReactRootView != null) {
      throw new IllegalStateException("Cannot loadApp while app is already running.");
//Create react rootview as root view, which is essentially a FrameLayout
    mReactRootView = createRootView();
// Start the RN application and complete some initialization settings
// Use react rootview as the display view of Activity

Look at what react activity delegate does:

1. Create react rootview as root view
 2.startReactApplication start RN process
 3. Display the reactroot view as the content of ReactActivity

Therefore, ReactRootView is the key. Enter the ReactRootView class and continue to look at the startReactApplication method to start RN. It takes three parameters: ReactInstanceManager,appName, launch setting parameter launchOptions,

   * Schedule rendering of the react component rendered by the JS application from the given JS
   * module (@{param moduleName}) using provided {@param reactInstanceManager} to attach to the
   * JS context of that manager. Extra parameter {@param launchOptions} can be used to pass initial
   * properties for the react component.
  public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle initialProperties,
      @Nullable String initialUITemplate) {
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "startReactApplication");
    try {

      // TODO(6788889): Use POJO instead of bundle here, apparently we can't just use WritableMap
      // here as it may be deallocated in native after passing via JNI bridge, but we want to reuse
      // it in the case of re-creating the catalyst instance
        mReactInstanceManager == null,
        "This root view has already been attached to a catalyst instance manager");
    // React instancemanage instance, manage react instance
      mReactInstanceManager = reactInstanceManager;
   // js registered name, the same as ReactActivity.getMainComponentName() and AppRegistry.registerComponent()
      mJSModuleName = moduleName;
   // It is the data passed from Native to JS, which may be replaced by POJO in the future. It is null by default. If necessary, rewrite createReactActivityDelegate and getLaunchOptions method
      mAppProperties = initialProperties;
      mInitialUITemplate = initialUITemplate;

      if (mUseSurface) {
        // TODO initialize surface here
    // Create context ReactContext of RN
      if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
    //Add layout monitor after width height calculation

    } finally {

Next, go to the ReactInstanceManger class and take a look at the createReactContextInBackground method,

   * Trigger react context initialization asynchronously in a background async task. This enables
   * applications to pre-load the application JS, and execute global code before
   * {@link ReactRootView} is available and measured. This should only be called the first time the
   * application is set up, which is enforced to keep developers from accidentally creating their
   * application multiple times without realizing it.
   * Called from UI thread.
  public void createReactContextInBackground() {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContextInBackground()");
        "createReactContextInBackground should only be called when creating the react " +
            "application for the first time. When reloading JS, e.g. from a new file, explicitly" +
            "use recreateReactContextInBackground");
    // It is called only when the application is started for the first time to prevent developers from accidentally creating other applications
    mHasStartedCreatingInitialContext = true;

The createReactContextInBackground method will only be called when it is first started. When the app is reloaded, it will call recreateReactContextInBackground(). Both methods will call recreatereactcontextinbackground inner(),

  private void recreateReactContextInBackgroundInner() {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackgroundInner()");
        .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: recreateReactContextInBackground");
  //UI thread
//The development mode can update the Bundle online, shake the pop-up debugging menu and other functions. This part belongs to the debugging function process.
    if (mUseDeveloperSupport && mJSMainModulePath != null) {
      final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();

      // If remote JS debugging is enabled, load from dev server.
      if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
          !devSettings.isRemoteJSDebugEnabled()) {
        // If there is a up-to-date bundle downloaded from server,
        // with remote JS debugging disabled, always use that.
// Debug mode, load jsBundle from server

      if (!Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
// Load service bundle
        if (mBundleLoader == null) {
        } else {
              new PackagerStatusCallback() {
                public void onPackagerStatusFetched(final boolean packagerIsRunning) {
                      new Runnable() {
                        public void run() {
                          if (packagerIsRunning) {
                          } else {
                            // If dev server is down, disable the remote JS debugging.
    // Load local bundle

The recreateReactContextInBackgroundFromBundleLoader method calls the recreateReactContextInBackground method down

  private void recreateReactContextInBackground(
//Transfer station of bidirectional communication between C + + and JS
    JavaScriptExecutorFactory jsExecutorFactory,
// The bundle loader decides where to load the bundle file according to the configuration in ReactNativeHost
    JSBundleLoader jsBundleLoader) {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackground()");
    //Create ReactContextInitParams object
    final ReactContextInitParams initParams = new ReactContextInitParams(
    if (mCreateReactContextThread == null) {
  // Instantiate ReactContext in newThread
    } else {
      mPendingReactContextInitParams = initParams;

 //Content in runCreateReactContextOnNewThread() method
final ReactApplicationContext reactApplicationContext =

In the runCreateReactContextOnNewThread method, we see that the ReactInstanceManager.createReactContext method finally creates the ReactApplicationContext. Let's continue to see the createReactContext() method, which has two parameters:

JSCJavaScriptExecutor jsExecutor: JSCJavaScriptExecutor inherits from JavaScript executor. When this class is loaded, it will automatically load the "" Library and call the Native party
Method initHybrid() initializes the framework of communication between RN and JSC in C + + layer.
JSBundleLoader jsBundleLoader: it caches the information of JSBundle and encapsulates the relevant interfaces of the upper layer to load JSBundle. Catalyst instance calls react bridge to load JS file through its introduction. Different scenarios will be created
For different loaders, see JSBundleLoader.

private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor,
      JSBundleLoader jsBundleLoader) {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()");
    ReactMarker.logMarker(CREATE_REACT_CONTEXT_START, jsExecutor.getName());
// ReactApplicationContext is the wrapper class of ReactApplicationContext
    final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);

    NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null
        ? mNativeModuleCallExceptionHandler
        : mDevSupportManager;
//Create the JavaModule registry Builder, which is used to create the JavaModule registry. The JavaModule registry registers all javamodules in the CatalystInstance.
    NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
//After processing various parameters such as jsExecutor, nativeModuleRegistry, nativeModuleRegistry and so on, start to build CatalystInstanceImpl instance.
    CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
      .setJSExecutor(jsExecutor)// js execution communication class
      .setRegistry(nativeModuleRegistry)//java module registry
      .setJSBundleLoader(jsBundleLoader)// bundle loader
      .setNativeModuleCallExceptionHandler(exceptionHandler); // Exception handler

    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
    final CatalystInstance catalystInstance;
    try {
      catalystInstance =;
    } finally {
    if (mJSIModulePackage != null) {
        .getJSIModules(reactContext, catalystInstance.getJavaScriptContextHolder()));

    if (mBridgeIdleDebugListener != null) {
//Call the Native method of CatalystInstanceImpl to convert Java Registry to Json, and then transfer it from C + + layer to JS layer.
      catalystInstance.setGlobalVariable("__RCTProfileIsProfiling", "true");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
//Start to load JS Bundle through CatalystInstance
  //Associate realcontext with CatalystInstance

    return reactContext;

Use catalystInstance.runJSBundle() in createReactContext method to load JS bundle

  public void runJSBundle() {
   ...Ellipsis code

Look at the loadScript method and the implementation class CatalystInstanceImpl of the parameter JSBundleLoaderDelegate interface. Let's assume that the loadScriptFromAssets method is called,

  public void loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously) {
    mSourceURL = assetURL;
    jniLoadScriptFromAssets(assetManager, assetURL, loadSynchronously);
private native void jniLoadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously);

Finally, calls CatalystInstanceImpl.cpp of C + + layer to load JS Bundle.
Next, let's look at the construction method of the implementation class CatalystInstanceImpl of CatalystInstance:

private CatalystInstanceImpl(
      final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry nativeModuleRegistry,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstanceImpl");
  //The Native method is used to create JNI related states and return mHybridData
    mHybridData = initHybrid();
 //The three threads in RN: Native Modules Thread, JS Thread and UI Thread are all managed by Handler.
    mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
        new NativeExceptionHandler());
    mBridgeIdleListeners = new CopyOnWriteArrayList<>();
    mNativeModuleRegistry = nativeModuleRegistry;
    mJSModuleRegistry = new JavaScriptModuleRegistry();
    mJSBundleLoader = jsBundleLoader;
    mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
    mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread();
    mTraceListener = new JSProfilerTraceListener(this);

    Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge before initializeBridge");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeCxxBridge");
//Native method, call the initializeBridge() method, create a BridgeCallback instance, and initialize the Bridge.
      new BridgeCallback(this),
    Log.d(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");

    mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());

//Initialize the communication bridge in the C + + layer
 private native void initializeBridge(
      ReactCallback callback,
      JavaScriptExecutor jsExecutor,
      MessageQueueThread jsQueue,
      MessageQueueThread moduleQueue,
      Collection<JavaModuleWrapper> javaModules,
      Collection<ModuleHolder> cxxModules);

Parameter interpretation:

  • ReactCallback: the static internal class ReactCallback of catalystinstanceimpl, which is responsible for interface callback
  • JavaScript executor: js executor, passing the call of js to the c + + layer
  • MessageQueueThread jsQueue: js thread
  • MessageQueueThread moduleQueue: java thread
  • javaModules: java module
  • cxxModules: c++ module

I'm so tired. Continue. Let's go to the c + + layer and have a look. CatalystInstanceImpl.cpp can be found in the project's node_modules / react native / react Android / SRC / main / JNI / react / JNI
Take a look at CatalystInstanceImpl::jniLoadScriptFromAssets

void CatalystInstanceImpl::jniLoadScriptFromAssets(
    jni::alias_ref<JAssetManager::javaobject> assetManager,
    const std::string& assetURL,
    bool loadSynchronously) {
  const int kAssetsLength = 9;  // strlen("assets://");
//Get the pathname of the source js Bundle. The default is
  auto sourceURL = assetURL.substr(kAssetsLength);
//Assetmanager is the assetmanager passed from the Java layer. Call the extractAssetManager() method in jsload.cpo, and then extract assetmanager()
  //Call the asset manager? Fromjava() method in Android / asset? Manager? JNI. H to get the AssetManager object.
  auto manager = extractAssetManager(assetManager);
// Call the loadScriptFromAssets method of JSloader.cpp to read the contents of js Bundle
  auto script = loadScriptFromAssets(manager, sourceURL);
// Unpacking judgment of un bundle command. By default, build.gradle is the bundling method.
  if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
    auto bundle = JniJSModulesUnbundle::fromEntryFile(manager, sourceURL);
    auto registry = RAMBundleRegistry::singleBundleRegistry(std::move(bundle));
  } else if (Instance::isIndexedRAMBundle(&script)) {
    instance_->loadRAMBundleFromString(std::move(script), sourceURL);
  } else {
//The bundle command packs this process. Instance? Is an instance of the class in instance. H
    instance_->loadScriptFromString(std::move(script), sourceURL, loadSynchronously);

In the cxxReact NativeToJsBridge.cpp file of the project node [modules / react native / react common:

void NativeToJsBridge::loadApplication(
    std::unique_ptr<JSModulesUnbundle> unbundle,
    std::unique_ptr<const JSBigString> startupScript,
    std::string startupScriptSourceURL) {

  //Get a MessageQueueThread, and execute a Task in the thread.
        (JSExecutor* executor) mutable {

    auto unbundle = unbundleWrap.move();
    if (unbundle) {

    //The executor is obtained from the map returned by runonexecutiorqueue(), which corresponds to JSCJavaScriptExecutorHolder in OnLoad, and also corresponds to
    //JSCJavaScriptExecutor in Java. Its instance is implemented in jsiexecutter.cpp.
//About the unbundle command

<unbundle Commands, how to use and bundle The commands are exactly the same. unbundle The order is in bundle Added a function based on the command, in addition to generating integration JS file Besides,
//Generate individual unconsolidated JS files (which will be optimized) and put them all in the JS modules directory. At the same time, an identity file named unbound will be generated and put in it. UNBUNDLE identifies the first four bytes of the file
//Fixed to 0xFB0BD1E5 for verification before loading.

Enter the project node [modules / react native / react common / jsiexecution / jsireact / jsiexecution.cpp to call the loadApplicationScript() method of jsiexecution.cpp.

//Explain and execute JS
      std::make_unique<BigStringBuffer>(std::move(script)), sourceURL);

The flushedQueueJS branch line is the flushedQueue() method of MessageQueue.js. At this time, JS has been loaded into the queue, waiting for the Java layer to drive it. After loading JS, return to react application context, and we will continue to follow up its implementation.
We go back to the runCreateReactContextOnNewThread method of the ReactInstanceManager class and see the setupReactContext() method. After entering, we can see the attachRootViewToInstance(reactRoot) method. After entering

private void attachRootViewToInstance(final ReactRoot reactRoot) {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.attachRootViewToInstance()");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachRootViewToInstance");
    UIManager uiManagerModule = UIManagerHelper.getUIManager(mCurrentReactContext, reactRoot.getUIManagerType());

    @Nullable Bundle initialProperties = reactRoot.getAppProperties();
    final int rootTag = uiManagerModule.addRootView(
      initialProperties == null ?
            new WritableNativeMap() : Arguments.fromBundle(initialProperties),
  // Start entry
        new Runnable() {
          public void run() {
                TRACE_TAG_REACT_JAVA_BRIDGE, "pre_rootView.onAttachedToReactInstance", rootTag);

catalystInstance.getJSModule(AppRegistry.class)AppRegistry.class is the interface method exposed by JS layer to Java layer. Its real implementation is in AppRegistry.js, which is the JS layer entry to run all RN applications,

runApplication(appKey: string, appParameters: any): void {
    const msg =
      'Running application "' +
      appKey +
      '" with appParams: ' +
      JSON.stringify(appParameters) +
      '. ' +
      '__DEV__ === ' +
      String(__DEV__) +
      ', development-level warning are ' +
      (__DEV__ ? 'ON' : 'OFF') +
      ', performance optimizations are ' +
      (__DEV__ ? 'OFF' : 'ON');
      'AppRegistry.runApplication' + runCount++,
      () => msg,
      runnables[appKey] && runnables[appKey].run,
      'Application ' +
        appKey +
        ' has not been registered.\n\n' +
        "Hint: This error often happens when you're running the packager " +
        '(local dev server) from a wrong folder. For example you have ' +
        'multiple apps and the packager is still running for the app you ' +
        'were working on before.\nIf this is the case, simply kill the old ' +
        'packager instance (e.g. close the packager terminal window) ' +
        'and start the packager in the correct app folder (e.g. cd into app ' +
        "folder and run 'npm start').\n\n" +
        'This error can also happen due to a require() error during ' +
        'initialization or failure to call AppRegistry.registerComponent.\n\n',

    SceneTracker.setActiveScene({name: appKey});

😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀😀. A kind of
Reading the source code is still a time-consuming thing, ha ha.

Update to personal public address and website simultaneously.
Personal website:
Public number: Junwei said.

Tags: Android React Java Javascript

Posted on Thu, 07 Nov 2019 06:11:17 -0500 by slpctrl