I. Initialization process

Before we start looking at the startup process, let’s start with the hybrid development process, which is roughly two-step. 1. Inherit ReactActivity and complete related initialization.

public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
    private ReactRootView mReactRootView;
    private ReactInstanceManager mReactInstanceManager;

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

        mReactRootView = new ReactRootView(this);
        mReactInstanceManager = ReactInstanceManager.builder()
                .setApplication(getApplication())
                .setCurrentActivity(this)
                .setBundleAssetName("index.android.bundle")
                .setJSMainModulePath("index")
                .addPackage(new MainReactPackage())
                .setUseDeveloperSupport(BuildConfig.DEBUG)
                .setInitialLifecycleState(LifecycleState.RESUMED)
                .build();
        // The string here (e.g. "MyReactNativeApp") has to match
        // the string in AppRegistry.registerComponent() in index.js
        mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);

        setContentView(mReactRootView);
    }

    @Override
    public void invokeDefaultOnBackPressed() {
        super.onBackPressed();
    }
    
    @Override
    protected void onPause() {
        super.onPause();
    
        if(mReactInstanceManager ! = null) { mReactInstanceManager.onHostPause(this); } } @Override protected voidonResume() {
        super.onResume();
    
        if(mReactInstanceManager ! = null) { mReactInstanceManager.onHostResume(this, this); } } @Override protected voidonDestroy() {
        super.onDestroy();
    
        if(mReactInstanceManager ! = null) { mReactInstanceManager.onHostDestroy(this); }if(mReactRootView ! = null) { mReactRootView.unmountReactApplication(); } } @Override public voidonBackPressed() {
        if(mReactInstanceManager ! = null) { mReactInstanceManager.onBackPressed(); }else{ super.onBackPressed(); }}}Copy the code

Note: a. In actual development, a singleton will be written to obtain the mReactInstanceManager object. Multiple ReactFragments or ReactActivities can share the same mReactInstanceManager object. B. ReactRootView, as the root layout of the ReactActivity, is the container View to which our React code was attached.

2. Write React code

import React from 'react';
import {AppRegistry, StyleSheet, Text, View} from 'react-native';

class HelloWorld extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.hello}>Hello, World</Text>
      </View>
    );
  }
}
var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  hello: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
});

AppRegistry.registerComponent('MyReactNativeApp', () => HelloWorld);
Copy the code

After completing the above 2 steps and adding some configuration operations, you should be able to get a demo run up and running.

Second, the startup process

The startup process is divided into the following steps:

1 At startup, in the onCreate() function of the ReactActivity, we create a ReactInstanceManager object. The ReactInstanceManager manages the lifecycle, manages the ReactRootView, And some configurations. 2 ReactRootView as root view of the application, by calling the ReactRootView. StartReactApplication () method to start the application. Before 3 RN application page rendering, need to create ReactContext creation process in, asynchronous task ReactContextInitAsyncTask responsible to complete the task. 4 ReactContextInitAsyncTask ReactContextInitAsyncTask in the background. The doInBackground () perform ReactContext created, in the process of creating ReactContext, JavaScriptModuleRegistry and NativeModuleRegistry registries and their management classes CatalystInstanceImpl are created based on the ReactPackage, and JS, Native, and UI thread queues are created. And eventually call CatalystInstanceImpl. RunJSBundle () to the asynchronous loading JS Bundle files. 5 background, after the completion of tasks in ReactContextInitAsyncTask. OnPostExecute () will call ReactInstanceManager. SetupReactContext () is set to create a good ReactContext, Load the ReactRootView in and call APPRegistry, the JS entry to the RN application, to launch the application. The JS layer finds the corresponding startup component that has been registered and executes renderApplication() to render the entire application.Copy the code

Following the initialization process, we start with ReactActivity. Looking at the source code, we can see that the ReactActivity doesn’t do much. The delegate class ReactActivityDelegate does most of the work. Let’s look at what the ReactActivityDelegate class does.

public class ReactActivityDelegate {

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

  private @Nullable ReactRootView mReactRootView;
  private @Nullable DoubleTapReloadRecognizer mDoubleTapReloadRecognizer;
  private @Nullable PermissionListener mPermissionListener;
  private @Nullable Callback mPermissionsCallback;

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

  protected ReactNativeHost getReactNativeHost() {
    return ((ReactApplication) getPlainActivity().getApplication()).getReactNativeHost();
  }

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

  protected void onCreate(Bundle savedInstanceState) {
    String mainComponentName = getMainComponentName();
    if(mainComponentName ! = null) { loadApp(mainComponentName); } 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); }}Copy the code

You can see that the ReactActivityDelegate does three things when it’s created:

1. Create a ReactRootView. 2. callsetThe ContentView method uses mReactRootView as the Content view of the ReactActivityCopy the code

It is not difficult to find that the real core of ReactNative is ReactRootView. ReactRootView is essentially a FrameLayout, and there is no magic. Ok, let’s look at jumping into ReactRootView to continue our startup process analysis.

public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle initialProperties,
      @Nullable String initialUITemplate) {
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "startReactApplication");
    try {
      UiThreadUtil.assertOnUiThread();

      // 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 Assertions.assertCondition( mReactInstanceManager == null, "This root view has already been attached to a catalyst instance manager"); mReactInstanceManager = reactInstanceManager; mJSModuleName = moduleName; mAppProperties = initialProperties; mInitialUITemplate = initialUITemplate; if (mUseSurface) { // TODO initialize surface here } if (! mReactInstanceManager.hasStartedCreatingInitialContext()) { mReactInstanceManager.createReactContextInBackground(); } attachToReactInstanceManager(); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); }}Copy the code

In this method, we create the application context, ReactContext, and assign the passed parameters to the member variables. Let’s take a look at these four parameters:

ReactInstanceManager ReactInstanceManager: Manages React instances. String moduleName: the name of the module, corresponding ReactActivity getMainComponentName () and AppRegistry. RegisterComponent (). Bundle initialProperties: Data of type Bundle that can be passed to the JS layer during startActivity(). String initialUITemplate: the 0.59 version does not use the String initialUITemplate.Copy the code

Continue our start-up process, ReactInstanceManager createReactContextInBackground (). The name seems to be creating a context in the background thread, so let’s jump in and see.

@ThreadSafe
public class ReactInstanceManager {
    @ThreadConfined(UI)
  public void createReactContextInBackground() {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContextInBackground()"); Assertions.assertCondition( ! mHasStartedCreatingInitialContext,"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");

    mHasStartedCreatingInitialContext = true;
    recreateReactContextInBackgroundInner();
  }
  
  @ThreadConfined(UI)
  public void recreateReactContextInBackground() {
    Assertions.assertCondition(
        mHasStartedCreatingInitialContext,
        "recreateReactContextInBackground should only be called after the initial " +
            "createReactContextInBackground call.");
    recreateReactContextInBackgroundInner();
  }

  @ThreadConfined(UI)
  private void recreateReactContextInBackgroundInner() {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackgroundInner()");
    PrinterHolder.getPrinter()
        .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: recreateReactContextInBackground");
    UiThreadUtil.assertOnUiThread();

    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. onJSBundleLoadedFromServer(null);return;
      }

      if(! Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {if (mBundleLoader == null) {
          mDevSupportManager.handleReloadJS();
        } else {
          mDevSupportManager.isPackagerRunning(
              new PackagerStatusCallback() {
                @Override
                public void onPackagerStatusFetched(final boolean packagerIsRunning) {
                  UiThreadUtil.runOnUiThread(
                      new Runnable() {
                        @Override
                        public void run() {
                          if (packagerIsRunning) {
                            mDevSupportManager.handleReloadJS();
                          } else {
                            // If dev server is down, disable the remote JS debugging.
                            devSettings.setRemoteJSDebugEnabled(false); recreateReactContextInBackgroundFromBundleLoader(); }}}); }}); }return;
      }
    }

    recreateReactContextInBackgroundFromBundleLoader();
  }

  @ThreadConfined(UI)
  private void recreateReactContextInBackgroundFromBundleLoader() {
    Log.d(
      ReactConstants.TAG,
      "ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()");
    PrinterHolder.getPrinter()
        .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from BundleLoader");
    recreateReactContextInBackground(mJavaScriptExecutorFactory, mBundleLoader);
  }
  
  @ThreadConfined(UI)
  private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()");
    UiThreadUtil.assertOnUiThread();
    synchronized (mAttachedReactRoots) {
      synchronized (mReactContextLock) {
        if(mCurrentReactContext ! = null) { tearDownReactContext(mCurrentReactContext); mCurrentReactContext = null; } } } mCreateReactContextThread = new Thread( null, newRunnable() {
              @Override
              public void run() {
                ReactMarker.logMarker(REACT_CONTEXT_THREAD_END);
                synchronized (ReactInstanceManager.this.mHasStartedDestroying) {
                  while (ReactInstanceManager.this.mHasStartedDestroying) {
                    try {
                      ReactInstanceManager.this.mHasStartedDestroying.wait();
                    } catch (InterruptedException e) {
                      continue;
                    }
                  }
                }
                // As destroy() may have run and set this to false, ensure that it is true before we create
                mHasStartedCreatingInitialContext = true;

                try {
                  Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
                  ReactMarker.logMarker(VM_INIT);
                  final ReactApplicationContext reactApplicationContext =
                      createReactContext(
                          initParams.getJsExecutorFactory().create(),
                          initParams.getJsBundleLoader());

                  mCreateReactContextThread = null;
                  ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_START);
                  final Runnable maybeRecreateReactContextRunnable =
                      new Runnable() {
                        @Override
                        public void run() {
                          if(mPendingReactContextInitParams ! = null) { runCreateReactContextOnNewThread(mPendingReactContextInitParams); mPendingReactContextInitParams = null; }}}; Runnable setupReactContextRunnable = newRunnable() {
                        @Override
                        public void run() { try { setupReactContext(reactApplicationContext); } catch (Exception e) { mDevSupportManager.handleException(e); }}}; reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable); UiThreadUtil.runOnUiThread(maybeRecreateReactContextRunnable); } catch (Exception e) { mDevSupportManager.handleException(e); }}},"create_react_context");
    ReactMarker.logMarker(REACT_CONTEXT_THREAD_START);
    mCreateReactContextThread.start();
  }
   private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor,
      JSBundleLoader jsBundleLoader) {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()"); ReactMarker.logMarker(CREATE_REACT_CONTEXT_START, jsExecutor.getName()); // Create ReactContext here, ReactApplicationContext inherits from ReactContext final ReactApplicationContext ReactContext = new ReactApplicationContext(mApplicationContext); / / check to see if the external set NativeModuleCallExceptionHandler, It is in the building ReactInstanceManager ReactInstanceManagerBuilder is passed in / / use the external NativeModuleCallExceptionHandler if set, If not, use DevSupportManager. NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler ! = null ? mNativeModuleCallExceptionHandler : mDevSupportManager; reactContext.setNativeModuleCallExceptionHandler(exceptionHandler); NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages,false); //jsExecutor, nativeModuleRegistry, nativeModuleRegistry, etc. After processing the parameters, start building the CatalystInstanceImpl instance. CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder() .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault()) .setJSExecutor(jsExecutor) .setRegistry(nativeModuleRegistry) .setJSBundleLoader(jsBundleLoader) .setNativeModuleCallExceptionHandler(exceptionHandler); ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START); // CREATE_CATALYST_INSTANCE_END isin JSCExecutor.cpp
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
    final CatalystInstance catalystInstance;
    try {
      catalystInstance = catalystInstanceBuilder.build();
    } finally {
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
      ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);
    }
    if(mJSIModulePackage ! = null) { catalystInstance.addJSIModules(mJSIModulePackage .getJSIModules(reactContext, catalystInstance.getJavaScriptContextHolder())); }if(mBridgeIdleDebugListener ! = null) { catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener); }if (Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
      catalystInstance.setGlobalVariable("__RCTProfileIsProfiling"."true");
    }
    ReactMarker.logMarker(ReactMarkerConstants.PRE_RUN_JS_BUNDLE_START);
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle"); / / load JS Bundle catalystInstance. RunJSBundle (); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); / / associated with CatalystInstance ReacContext reactContext. InitializeWithInstance (CatalystInstance);returnreactContext; } private NativeModuleRegistry processPackages( ReactApplicationContext reactContext, List<ReactPackage> packages, Boolean checkAndUpdatePackageMembership) {/ / create JavaModule registry Builder, is used to create JavaModule registry, The JavaModule registry registers all JavaModule files with CatalystInstance. NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder( reactContext, this); ReactMarker.logMarker(PROCESS_PACKAGES_START); // TODO(6818138): Solve use-case of native modules overriding synchronized (mPackages) {for (ReactPackage reactPackage : packages) {
        if (checkAndUpdatePackageMembership && mPackages.contains(reactPackage)) {
          continue;
        }
        Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "createAndProcessCustomReactPackage");
        try {
          if(checkAndUpdatePackageMembership) { mPackages.add(reactPackage); } // Recursively handle the ReactPackage we inject in the Application by adding the respective modules to the corresponding registry. processPackage(reactPackage, nativeModuleRegistryBuilder); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } } } ReactMarker.logMarker(PROCESS_PACKAGES_END); ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_START); Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE,"buildNativeModuleRegistry"); NativeModuleRegistry nativeModuleRegistry; Try {/ / generate Java Module nativeModuleRegistry = nativeModuleRegistryBuilder registry. The build (); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END); }returnnativeModuleRegistry; }}Copy the code

Tossing and turning, and ultimately through runCreateReactContextOnNewThread opened a thread, Then call createReactContext (JavaScriptExecutor jsExecutor, JSBundleLoader JSBundleLoader) created ReactContext. Let’s focus on the 2 given argument passed in:

JavaScriptExecutor jsExecutor: When this class is loaded, it is automatically loaded"reactnativejnifb.so"Library, and will call the Native method initHybrid() to initialize C++ layer RN and JSC communication framework. JSBundleLoader JSBundleLoader: JSBundle caches JSBundle information and encapsulates the interface used by the upper layer to load Jsbundles. CatalystInstance calls ReactBridge to load JS files through its introduction. Different scenarios create different loaders.Copy the code

CreateReactContext ->processPackages does the following:

1 Create a JavaModule registry and assign it to CatalystInstance for management. 3 Process the ReactPackage and place the JavaModule in the appropriate registry. 3 Create CatalystInstance. 4 Associate ReactContext with CatalystInstance and load the JS Bundle.Copy the code

Then see catalystInstance. RunJSBundle (), the call stack of the method is as follows:

CatalystInstanceImpl.runJSBundle() -> JSBundleLoader.loadScript() ->CatalystInstanceImpl.loadScriptFromAssets()/loadScriptFromFile() -> CatalystInstanceImpl.jniLoadScriptFromAssets()/jniLoadScriptFromFile()
-> CatalystInstanceImpl::jniLoadScriptFromAssets()/jniLoadScriptFromFile() -> Instance::loadScriptFromString()/loadScriptFromFile()
-> NativeToJsBridge::loadApplication() -> JSCExecutor::loadApplicationScript()
Copy the code

JSCExecutor. CPP in C++ is used to load the JS Bundle. The core logic is in JSCExecutor.

public class CatalystInstanceImpl implements 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"); // Native method, used to create JNI related state and return mHybridData mHybridData = initHybrid(); // Create three core threads: Native Modules Thread, JS Thread, UI Thread, all managed by Handler. mReactQueueConfiguration = ReactQueueConfigurationImpl.create( reactQueueConfigurationSpec, new NativeExceptionHandler()); mBridgeIdleListeners = new CopyOnWriteArrayList<>(); mNativeModuleRegistry = nativeModuleRegistry; // Create the JavaScriptModule registry mJSModuleRegistry = new JavaScriptModuleRegistry(); mJSBundleLoader = jsBundleLoader; mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler; mNativeModulesQueueThread = mReactQueueConfiguration.getNativeModulesQueueThread(); mTraceListener = new JSProfilerTraceListener(this); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); Log.d(ReactConstants.TAG,"Initializing React Xplat Bridge before initializeBridge");
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "initializeCxxBridge"); // Create a BridgeCallback instance to initialize the Bridge. initializeBridge( new BridgeCallback(this), jsExecutor, mReactQueueConfiguration.getJSQueueThread(), mNativeModulesQueueThread, mNativeModuleRegistry.getJavaModules(this), mNativeModuleRegistry.getCxxModules()); Log.d(ReactConstants.TAG,"Initializing React Xplat Bridge after initializeBridge");
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);

    mJavaScriptContextHolder = new JavaScriptContextHolder(getJavaScriptContext());
  }
  
  private native void initializeBridge(
      ReactCallback callback,
      JavaScriptExecutor jsExecutor,
      MessageQueueThread jsQueue,
      MessageQueueThread moduleQueue,
      Collection<JavaModuleWrapper> javaModules,
      Collection<ModuleHolder> cxxModules);
}
Copy the code

The CatalystInstanceImpl is a wrapper management class that encapsulates various registries and initializes JNI. Let’s look at the six parameters passed to initialize the Bridge:

ReactCallback Callback: The static inner class ReactCallback of CatalystInstanceImpl, responsible for interface callbacks. JavaScriptExecutor jsExecutor: JS executor that passes JS calls to the C++ layer. MessageQueueThread jsQueue. GetJSQueueThread () : JS thread, through mReactQueueConfiguration. GetJSQueueThread (), Create mReactQueueConfiguration through ReactQueueConfigurationSpec. CreateDefault (). MessageQueueThread moduleQueue: Native threads, through mReactQueueConfiguration. GetNativeModulesQueueThread (), Create mReactQueueConfiguration through ReactQueueConfigurationSpec. CreateDefault (). Collection < JavaModuleWrapper > javaModules: Java modules, from mJavaRegistry. GetJavaModules (this). Collection < ModuleHolder > cxxModules) : c + + modules, from mJavaRegistry. GetCxxModules ().Copy the code

We went on to look at, CatalystInstanceImpl. RunJSBundle ()

@Override
  public void runJSBundle() {
    Log.d(ReactConstants.TAG, "CatalystInstanceImpl.runJSBundle()"); Assertions.assertCondition(! mJSBundleHasLoaded,"JS bundle was already loaded!"); // incrementPendingJSCalls(); // Call the loader to load the JS Bundle. mJSBundleLoader.loadScript(CatalystInstanceImpl.this); synchronized (mJSCallsPendingInitLock) { // Loading the bundle is queued on the JS thread, but may not have // run yet. It's safe to set this here, though, since any work it // gates will be queued on the JS thread behind the load. mAcceptCalls = true; for (PendingJSCall function : mJSCallsPendingInit) { function.call(this); } mJSCallsPendingInit.clear(); mJSBundleHasLoaded = true; } // This is registered after JS starts since it makes a JS call Systrace.registerListener(mTraceListener); }Copy the code

Then call JSBundleLoader loadScript

public abstract class JSBundleLoader {
    /**
   * This loader is recommended one for release version of your app. In that case local JS executor
   * should be used. JS bundle will be read from assets inNative code to save on passing large * strings from Java to native memory. */ / JSBundleLoader createAssetLoader( final Context context, final String assetUrl, final boolean loadSynchronously) {return new JSBundleLoader() {
      @Override
      public String loadScript(JSBundleLoaderDelegate delegate) {
        delegate.loadScriptFromAssets(context.getAssets(), assetUrl, loadSynchronously);
        returnassetUrl; }}; } /** * This loader loads bundle from file system. The bundle will beread in native code to save on
   * passing large strings from java to native memory.
   */
  public static JSBundleLoader createFileLoader(final String fileName) {
    return createFileLoader(fileName, fileName, false); } public static JSBundleLoader createFileLoader(final String fileName, final String assetUrl, final boolean loadSynchronously) {return new JSBundleLoader() {
      @Override
      public String loadScript(JSBundleLoaderDelegate delegate) {
        delegate.loadScriptFromFile(fileName, assetUrl, loadSynchronously);
        returnfileName; }}; } /** * This loader is used when bundle gets reloaded from dev server. In thatcase loader expect JS
   * bundle to be prefetched and stored in local file. We do that to avoid passing large strings
   * between java and native code and avoid allocating memory in java to fit whole JS bundle in it.
   * Providing correct {@param sourceURL} of downloaded bundle is required for JS stacktraces to
   * work correctly and allows for sourceMaps to correctly symbolize those. * / / / from the cache loading public static JSBundleLoader createCachedBundleFromNetworkLoader (final StringsourceURL,
      final String cachedFileLocation) {
    return new JSBundleLoader() {
      @Override
      public String loadScript(JSBundleLoaderDelegate delegate) {
        try {
          delegate.loadScriptFromFile(cachedFileLocation, sourceURL, false);
          return sourceURL; } catch (Exception e) { throw DebugServerException.makeGeneric(e.getMessage(), e); }}}; } /** * This loader is used to load delta bundles from the dev server. We pass each delta message to * the loader and process itin C++. Passing it as a string leads to inefficiencies due to memory
   * copies, which will have to be addressed inA follow - up. * @ param nativeDeltaClient * / / / from the dev server load public static JSBundleLoader createDeltaFromNetworkLoader ( final StringsourceURL,
    final NativeDeltaClient nativeDeltaClient) {
    return new JSBundleLoader() {
      @Override
      public String loadScript(JSBundleLoaderDelegate delegate) {
        try {
          delegate.loadScriptFromDeltaBundle(sourceURL, nativeDeltaClient, false);
          return sourceURL; } catch (Exception e) { throw DebugServerException.makeGeneric(e.getMessage(), e); }}}; } /** * This loader is used when proxy debugging is enabled. In thatcase there is no point in fetching
   * the bundle from device as remote executor will have to doIt anyway. * / / / when the debug load public static JSBundleLoader createRemoteDebuggerBundleLoader (final String proxySourceURL, final String realSourceURL) {return new JSBundleLoader() {
      @Override
      public String loadScript(JSBundleLoaderDelegate delegate) {
        delegate.setSourceURLs(realSourceURL, proxySourceURL);
        returnrealSourceURL; }}; } /** Loads the script, returning the URL of thesource it loaded. */
  public abstract String loadScript(JSBundleLoaderDelegate delegate);
}
Copy the code

Next we to CatalystInstanceImpl. LoadScriptFromAssets (), for example.

@Override
  public void loadScriptFromAssets(AssetManager assetManager, String assetURL, boolean loadSynchronously) {
    mSourceURL = assetURL;
    jniLoadScriptFromAssets(assetManager, assetURL, loadSynchronously);
  }
Copy the code

As you can see, the method ends up calling the Native jniLoadScriptFromAssets to load the JS Bundle and go to CatalystInstanceImpl. CPP.

void CatalystInstanceImpl::jniLoadScriptFromAssets(
    jni::alias_ref<JAssetManager::javaobject> assetManager,
    const std::string& assetURL,
    bool loadSynchronously) {
  const int kAssetsLength = 9;  // strlen("assets://"); / / to getsourceJs Bundle path name autosourceURL = assetURL.substr(kAssetsLength); // Get the AssetManager object. auto manager = extractAssetManager(assetManager); // Read the contents of the JS Bundle. auto script = loadScriptFromAssets(manager,sourceURL); Build. gradle is bundled by default.if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {
    auto bundle = JniJSModulesUnbundle::fromEntryFile(manager, sourceURL);
    auto registry = RAMBundleRegistry::singleBundleRegistry(std::move(bundle));
    instance_->loadRAMBundle(
      std::move(registry),
      std::move(script),
      sourceURL,
      loadSynchronously);
    return;
  } elseInstance_ ->loadScriptFromString(STD ::move(script),sourceURL, loadSynchronously); }} The unbundle <unbundle command is used in exactly the same way as the bundle command. The unbundle command is an addition to the bundle command. In addition to generating the integrated JS file index.android.bundle, the unbundle command also generates individual unintegrated JS files (which will be optimized) in the js-modules directory. An identity file called UNBUNDLE is generated and stored in it. UNBUNDLE The first four bytes of a file are fixed as 0xFB0BD1E5 and are used for verification before loading.Copy the code

The loadScriptFromString() method of instance.cpp is then called to parse the contents of the JS Bundle.

void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,
                                    std::string sourceURL,
                                    bool loadSynchronously) {
  SystraceSection s("Instance::loadScriptFromString"."sourceURL".sourceURL);
  if (loadSynchronously) {
    loadApplicationSync(nullptr, std::move(string), std::move(sourceURL));
  } else {
    loadApplication(nullptr, std::move(string), std::move(sourceURL)); }}Copy the code

The loadApplication() method of nativeTojsbridge.cpp is further called, which is implemented as follows:

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

  runOnExecutorQueue(
      [this,
       bundleRegistryWrap=folly::makeMoveWrapper(std::move(bundleRegistry)),
       startupScript=folly::makeMoveWrapper(std::move(startupScript)),
       startupScriptSourceURL=std::move(startupScriptSourceURL)]
        (JSExecutor* executor) mutable {
    auto bundleRegistry = bundleRegistryWrap.move();
    if (bundleRegistry) {
      executor->setBundleRegistry(std::move(bundleRegistry)); } try {// JSCJavaScriptExecutor in Java corresponds. An instance of it is implemented in jsiExecutor.cpp. executor->loadApplicationScript(std::move(*startupScript), std::move(startupScriptSourceURL)); } catch (...) { m_applicationScriptHasFailure =true; throw; }}); }Copy the code

The loadApplicationScript() method of jsiExecutor.cpp is further called.

void JSIExecutor::loadApplicationScript(
    std::unique_ptr<const JSBigString> script,
    std::string sourceURL) {
  SystraceSection s("JSIExecutor::loadApplicationScript");

  // TODO: check for and use precompiled HBC

  runtime_->global().setProperty(
      *runtime_,
      "nativeModuleProxy",
      Object::createFromHostObject(
          *runtime_, std::make_shared<NativeModuleProxy>(*this)));

  runtime_->global().setProperty(
      *runtime_,
      "nativeFlushQueueImmediate",
      Function::createFromHostFunction(
          *runtime_,
          PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
          1,
          [this](
              jsi::Runtime &,
              const jsi::Value &,
              const jsi::Value *args,
              size_t count) {
            if(count ! = 1) { throw std::invalid_argument("nativeFlushQueueImmediate arg count must be 1");
            }
            callNativeModules(args[0], false);
            return Value::undefined();
          }));

  runtime_->global().setProperty(
      *runtime_,
      "nativeCallSyncHook",
      Function::createFromHostFunction(
          *runtime_,
          PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
          1,
          [this](
              jsi::Runtime &,
              const jsi::Value &,
              const jsi::Value *args,
              size_t count) { return nativeCallSyncHook(args, count); }));

  if (runtimeInstaller_) {
    runtimeInstaller_(*runtime_);
  }

  bool hasLogger(ReactMarker::logTaggedMarker);
  std::string scriptName = simpleBasename(sourceURL);
  if (hasLogger) {
    ReactMarker::logTaggedMarker( ReactMarker::RUN_JS_BUNDLE_START, scriptName.c_str()); EvaluateJavaScript (STD ::make_unique<BigStringBuffer>(STD ::move(script)),sourceURL);
  flush();
  if (hasLogger) {
    ReactMarker::logMarker(ReactMarker::CREATE_REACT_CONTEXT_STOP);
    ReactMarker::logTaggedMarker(
        ReactMarker::RUN_JS_BUNDLE_STOP, scriptName.c_str());
  }
}

void JSIExecutor::flush() {
  SystraceSection s("JSIExecutor::flush");
  if (flushedQueue_) {
    callNativeModules(flushedQueue_->call(*runtime_), true);
    return;
  }

  // When a native module is called from JS, BatchedBridge.enqueueNativeCall()
  // is invoked.  For that to work, require('BatchedBridge') has to be called,
  // and when that happens, __fbBatchedBridge is set as a side effect.
  Value batchedBridge =
      runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
  // So here, if __fbBatchedBridge doesn't exist, then we know no native calls // have happened, and we were able to determine this without forcing // BatchedBridge to be loaded as a side effect. if (! batchedBridge.isUndefined()) { // If calls were made, we bind to the JS bridge methods, And use them to // get the pending queue of native calls. CallNativeModules (flushedQueue_->call(*runtime_), true); } else if (delegate_) { // If we have a delegate, we need to call it; we pass a null list to // callNativeModules, since we know there are no native calls, without // calling into JS again. If no calls were made and there's no delegate,
    // nothing happens, which is correct.
    callNativeModules(nullptr, true);
  }
  
  void JSIExecutor::bindBridge() {
  std::call_once(bindFlag_, [this] {
    SystraceSection s("JSIExecutor::bindBridge (once)");
    Value batchedBridgeValue =
        runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
    if (batchedBridgeValue.isUndefined()) {
      throw JSINativeException(
          "Could not get BatchedBridge, make sure your bundle is packaged correctly");
    }

    Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
    callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "callFunctionReturnFlushedQueue");
    invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "invokeCallbackAndReturnFlushedQueue");
    flushedQueue_ =
        batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue");
    callFunctionReturnResultAndFlushedQueue_ =
        batchedBridge.getPropertyAsFunction(
            *runtime_, "callFunctionReturnResultAndFlushedQueue");
  });
}

void JSIExecutor::callNativeModules(const Value &queue, bool isEndOfBatch) {
  SystraceSection s("JSIExecutor::callNativeModules");
  // If this fails, you need to pass a fully functional delegate with a
  // module registry to the factory/ctor.
  CHECK(delegate_) << "Attempting to use native modules without a delegate";
#if 0 // maybe useful for debugging
  std::string json = runtime_->global().getPropertyAsObject(*runtime_, "JSON")
    .getPropertyAsFunction(*runtime_, "stringify").call(*runtime_, queue)
    .getString(*runtime_).utf8(*runtime_);
#endifM_delegate is the JsToNativeBridge object. delegate_->callNativeModules( *this, dynamicFromValue(*runtime_, queue), isEndOfBatch); }}Copy the code

The m_flushedQueueJS branch is the flushedQueue() method of Messagequieue.js, at which point the JS is loaded into the queue, waiting for the Java layer to drive it. After loading the JS, return the reactApplicationContext, and we’ll follow up with its implementation.

@ThreadConfined(UI)
  private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()");
    UiThreadUtil.assertOnUiThread();
    synchronized (mAttachedReactRoots) {
      synchronized (mReactContextLock) {
        if(mCurrentReactContext ! = null) { tearDownReactContext(mCurrentReactContext); mCurrentReactContext = null; } } } mCreateReactContextThread = new Thread( null, newRunnable() {
              @Override
              public void run() {
                ReactMarker.logMarker(REACT_CONTEXT_THREAD_END);
                synchronized (ReactInstanceManager.this.mHasStartedDestroying) {
                  while (ReactInstanceManager.this.mHasStartedDestroying) {
                    try {
                      ReactInstanceManager.this.mHasStartedDestroying.wait();
                    } catch (InterruptedException e) {
                      continue;
                    }
                  }
                }
                // As destroy() may have run and set this to false, ensure that it is true before we create
                mHasStartedCreatingInitialContext = true;

                try {
                  Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
                  ReactMarker.logMarker(VM_INIT);
                  final ReactApplicationContext reactApplicationContext =
                      createReactContext(
                          initParams.getJsExecutorFactory().create(),
                          initParams.getJsBundleLoader());

                  mCreateReactContextThread = null;
                  ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_START);
                  final Runnable maybeRecreateReactContextRunnable =
                      new Runnable() {
                        @Override
                        public void run() {
                          if(mPendingReactContextInitParams ! = null) { runCreateReactContextOnNewThread(mPendingReactContextInitParams); mPendingReactContextInitParams = null; }}}; Runnable setupReactContextRunnable = newRunnable() {
                        @Override
                        public void run() { try { setupReactContext(reactApplicationContext); } catch (Exception e) { mDevSupportManager.handleException(e); }}}; reactApplicationContext.runOnNativeModulesQueueThread(setupReactContextRunnable); UiThreadUtil.runOnUiThread(maybeRecreateReactContextRunnable); } catch (Exception e) { mDevSupportManager.handleException(e); }}},"create_react_context");
    ReactMarker.logMarker(REACT_CONTEXT_THREAD_START);
    mCreateReactContextThread.start();
  }
Copy the code

Then call setupReactContext(reactApplicationContext);

private void setupReactContext(final ReactApplicationContext reactContext) {
    Log.d(ReactConstants.TAG, "ReactInstanceManager.setupReactContext()");
    ReactMarker.logMarker(PRE_SETUP_REACT_CONTEXT_END);
    ReactMarker.logMarker(SETUP_REACT_CONTEXT_START);
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "setupReactContext"); synchronized (mAttachedReactRoots) { synchronized (mReactContextLock) { mCurrentReactContext = Assertions.assertNotNull(reactContext); } CatalystInstance catalystInstance = Assertions.assertNotNull(reactContext.getCatalystInstance()); / / Native Java module initialization catalystInstance. The initialize (); mDevSupportManager.onNewReactContextCreated(reactContext); mMemoryPressureRouter.addMemoryPressureListener(catalystInstance); / / reset moveReactContextToCurrentLifecycleState life cycle (); ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START); / / traverse ReactRootViewforReactRoot ReactRoot: mAttachedReactRoots {// ReactRootView attachRootViewToInstance(ReactRoot); } ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_END); } ReactInstanceEventListener[] listeners = new ReactInstanceEventListener[mReactInstanceEventListeners.size()]; final ReactInstanceEventListener[] finalListeners = mReactInstanceEventListeners.toArray(listeners); UiThreadUtil.runOnUiThread( newRunnable() {
          @Override
          public void run() {
            for(ReactInstanceEventListener listener : finalListeners) { listener.onReactContextInitialized(reactContext); }}}); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); ReactMarker.logMarker(SETUP_REACT_CONTEXT_END); reactContext.runOnJSQueueThread( newRunnable() {
          @Override
          public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
            ReactMarker.logMarker(CHANGE_THREAD_PRIORITY, "js_default"); }}); reactContext.runOnNativeModulesQueueThread( newRunnable() {
          @Override
          public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); }}); }Copy the code

ReactRootView attach operation.

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(); / / will ReactRootView as a root layout final int rootTag = uiManagerModule. AddRootView (reactRoot. GetRootViewGroup (), initialProperties == null ? new WritableNativeMap() : Arguments.fromBundle(initialProperties), reactRoot.getInitialUITemplate()); reactRoot.setRootViewTag(rootTag); // Start the process entry reactroot.runApplication (); Systrace.beginAsyncSection( TRACE_TAG_REACT_JAVA_BRIDGE,"pre_rootView.onAttachedToReactInstance",
      rootTag);
    UiThreadUtil.runOnUiThread(
        new Runnable() {
          @Override
          public void run() {
            Systrace.endAsyncSection(
                TRACE_TAG_REACT_JAVA_BRIDGE, "pre_rootView.onAttachedToReactInstance", rootTag); reactRoot.onStage(ReactStage.ON_ATTACH_TO_INSTANCE); }}); Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); }Copy the code

Reactroot.runapplication (), let’s follow through.

@Override
  public void runApplication() {
      Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactRootView.runApplication");
      try {
        if(mReactInstanceManager == null || ! mIsAttachedToInstance) {return;
        }

        ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
        if (reactContext == null) {
          return;
        }

        CatalystInstance catalystInstance = reactContext.getCatalystInstance();
        String jsAppModuleName = getJSModuleName();

        if (mUseSurface) {
          // TODO call surface's runApplication } else { if (mWasMeasured) { updateRootLayoutSpecs(mWidthMeasureSpec, mHeightMeasureSpec); } WritableNativeMap appParams = new WritableNativeMap(); appParams.putDouble("rootTag", getRootViewTag()); @Nullable Bundle appProperties = getAppProperties(); if (appProperties ! = null) { appParams.putMap("initialProps", Arguments.fromBundle(appProperties)); } if (getUIManagerType() == FABRIC) { appParams.putBoolean("fabric", true); } mShouldLogContentAppeared = true; catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams); } } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); }}Copy the code

ReactInstanceManager. AttachMeasuredRootViewToInstance () eventually entered the RN application startup process portal, Call catalystInstance. GetJSModule (AppRegistry. Class). RunApplication (jsAppModuleName, appParams). Appregistry. class is an interface method that the JS layer exposes to the Java layer. The 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');
    infoLog(msg);
    BugReporting.addSource('AppRegistry.runApplication' + runCount++, () => msg);
    invariant(
      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'); runnables[appKey].run(appParameters); },Copy the code

At this point, it will call JS to render the component, and then convert the JS component into an Android component through the UIManagerModule of the Java layer, and finally display it in the ReactRootView. We will see you next time.