RN Android端启动过程

RN SDK版本:0.63.2

安卓端RN启动流程分为启动和重启两个过程,启动不用多说,重启就是摇一摇出来的窗口中点击reload按钮后执行的流程。由于重启有明显的触发点,所以我选择从重启开始介绍。

一、安卓RN 重启过程

  1. 首先分析一下 handleReloadJS这个方法,这个在DevSupportManagerBase.java这个类里面,初次加载和重启时都会用到。
  @Override
  public void onPackagerReloadCommand() {
    // Disable debugger to resume the JsVM & avoid thread locks while reloading
    mDevServerHelper.disableDebugger();
    UiThreadUtil.runOnUiThread(
        new Runnable() {
          @Override
          public void run() {
            handleReloadJS();
          }
        });
  }

这个方法根据是否debug,会走不同分支:
1)debug时,走的是reloadJSInProxyMode, 这个方法会先创建JavaJSExecutor 然后执行ReactInstanceManager.java 的onReloadWithJSDebugger 方法;
2)没有debug 时,走的是reloadJSFromServer方法,这个方法会从服务器上下载bundle包,下载完之后执行ReactInstanceManager.java 的onJSBundleLoadedFromServer。

@Override
  public void handleReloadJS() {

    UiThreadUtil.assertOnUiThread();

    ReactMarker.logMarker(
        ReactMarkerConstants.RELOAD,
        mDevSettings.getPackagerConnectionSettings().getDebugServerHost());

    // dismiss redbox if exists
    hideRedboxDialog();

    if (mDevSettings.isRemoteJSDebugEnabled()) {
      PrinterHolder.getPrinter()
          .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Proxy");
      mDevLoadingViewController.showForRemoteJSEnabled();
      mDevLoadingViewVisible = true;
      reloadJSInProxyMode();
    } else {
      PrinterHolder.getPrinter()
          .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from Server");
      String bundleURL =
          mDevServerHelper.getDevServerBundleURL(Assertions.assertNotNull(mJSAppBundleName));
      reloadJSFromServer(bundleURL);
    }
  }

2.onJSBundleLoadedFromServer方法是加载本地缓存的临时包,其中临时包路径通过getDownloadedJSBundleFile这个接口方法获取,这个方法的实现在DevSupportManagerBase.java里面,临时包名称为ReactNativeDevBundle.js

  @ThreadConfined(UI)
  private void onJSBundleLoadedFromServer() {
    FLog.d(ReactConstants.TAG, "ReactInstanceManager.onJSBundleLoadedFromServer()");

    JSBundleLoader bundleLoader =
        JSBundleLoader.createCachedBundleFromNetworkLoader(
            mDevSupportManager.getSourceUrl(), mDevSupportManager.getDownloadedJSBundleFile());

    recreateReactContextInBackground(mJavaScriptExecutorFactory, bundleLoader);
  }
 private static final String JS_BUNDLE_FILE_NAME = "ReactNativeDevBundle.js";

二、安卓 RN 启动过程

1.先从 RNSingleInstance 的 init方法开始 ,这是我们自己写的类,主要是做添加module package,创建RN bridge,以及创建和重启bridge 过程的监听等,这里面调用了 createReactContextInBackground来创建RN环境。

2.createReactContextInBackground
这个逻辑很简单,只是根据一个bool 变量是否为空来控制后面的recreateReactContextInBackgroundInner方法只走一次。

  @ThreadConfined(UI)
  public void createReactContextInBackground() {
    FLog.d(TAG, "ReactInstanceManager.createReactContextInBackground()");
    UiThreadUtil
        .assertOnUiThread(); // Assert before setting mHasStartedCreatingInitialContext = true
    if (!mHasStartedCreatingInitialContext) {
      mHasStartedCreatingInitialContext = true;
      recreateReactContextInBackgroundInner();
    }
  }

3.recreateReactContextInBackgroundInner
这里面会根据mBundleLoader是否为空,来执行不同分支的逻辑,如果mBundleLoader为空,会走handleRealodJS 方法,重新创建mBundleLoader(因为mBundleLoader 在builder方法内就会被赋值,所以实际运行时没有走到过这个分支),正常情况下mBundleLoader不为空,会在一个runnable的 匿名线程里面执行bundle的加载。
加载的逻辑分为三个分支:
1)如果RN包正在运行,会调用handleReloadJS重新加载bridge,这里面会根据是否是debug模式来走对应的分支
2)如果缓存包已经是最新的,并且没有开启debug模式,则调用onJSBundleLoadedFromServer 从本地指定路径加载 RN包
3)其他情况,这时候应该是服务器挂掉了,需要禁用debug模式并调用 recreateReactContextInBackgroundFromBundleLoader方法重新创建bridge。

不管上面哪种情况,都会走recreateReactContextInBackgroundFromBundleLoader 加载bundle。

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

    if (mUseDeveloperSupport && mJSMainModulePath != null) {
      final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();

      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 (mDevSupportManager.hasUpToDateJSBundleInCache()
                              && !devSettings.isRemoteJSDebugEnabled()) {
                            // If there is a up-to-date bundle downloaded from server,
                            // with remote JS debugging disabled, always use that.
                            onJSBundleLoadedFromServer();
                          } else {
                            // If dev server is down, disable the remote JS debugging.
                            devSettings.setRemoteJSDebugEnabled(false);
                            recreateReactContextInBackgroundFromBundleLoader();
                          }
                        }
                      });
                }
              });
        }
        return;
      }
    }

    recreateReactContextInBackgroundFromBundleLoader();
  }

4.recreateReactContextInBackgroundFromBundleLoader.

这个方法打印了一行log,实际执行的是recreateReactContextInBackground,而这个方法内部会根据mCreateReactContextThread是否为空,来执行不同分支: 如果为空,则调用 mCreateReactContextThread方法,否则根据传进来的jsExecutorFactory和 jsBundleLoader来创建mPendingReactContextInitParams, 这个是用于重新创建bridge 所需的参数。

  @ThreadConfined(UI)
  private void recreateReactContextInBackgroundFromBundleLoader() {
    FLog.d(TAG, "ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader()");
    PrinterHolder.getPrinter()
        .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: load from BundleLoader");
    recreateReactContextInBackground(mJavaScriptExecutorFactory, mBundleLoader);
  }
  1. RunCreateReactContextOnNewThread
    这个方法启用了一个新的runnable线程来创建RN环境,如果此时正在销毁bridge,则等待销毁完成再执行后面的操作。然后根据前面创建的initParams来构造 JavaScriptExecutor,再把JavaScriptExecutor和JSBundleLoader 传入到createReactContext方法里面来执行最后的加载。
@ThreadConfined(UI)
  private void runCreateReactContextOnNewThread(final ReactContextInitParams initParams) {
    FLog.d(ReactConstants.TAG, "ReactInstanceManager.runCreateReactContextOnNewThread()");
    UiThreadUtil.assertOnUiThread();
    synchronized (mAttachedReactRoots) {
      synchronized (mReactContextLock) {
        if (mCurrentReactContext != null) {
          tearDownReactContext(mCurrentReactContext);
          mCurrentReactContext = null;
        }
      }
    }

    mCreateReactContextThread =
        new Thread(
            null,
            new Runnable() {
              @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);
                  JavaScriptExecutor javaScriptExecuto = null;
                  try {
                    javaScriptExecuto = initParams.getJsExecutorFactory().create();
                  } catch (Throwable e) {
                    mSoInitSucess = false;
                    return;
                  }
                  final ReactApplicationContext reactApplicationContext =
                      createReactContext(javaScriptExecuto,
                          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 =
                      new Runnable() {
                        @Override
                        public void run() {
                          try {
                            setupReactContext(reactApplicationContext);
                          } catch (Exception e) {
                            // TODO T62192299: remove this after investigation
                            FLog.e(
                                ReactConstants.TAG,
                                "ReactInstanceManager caught exception in setupReactContext",
                                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();
  }

这里提一下ReactMarker 这个类,是用来记录RN加载过程的一些关键节点的时间(具体参见ReactMarkerConstants ),主要用来做性能检测,里面也会发通知,可以用于获取流程中的各个关键节点。比如说我们项目里监听页面加载完成的通知,来控制loading的显示和隐藏。

ReactMarker.addListener(new ReactMarker.MarkerListener() {
                @Override
                public void logMarker(ReactMarkerConstants name, @Nullable String tag, int instanceKey) {
                    if(ReactMarkerConstants.CONTENT_APPEARED==name){
                        Activity topActivity = RnActivityStackManager.getInstance().getTopActivity();
                        if (topActivity instanceof RNActivity) {
                            ((RNActivity) topActivity).dismissLoading();
                        }
                    }
                }
            });

6.createReactContext

这里面根据传入的 JavaScriptExecutor和JSBundleLoader等参数构造一个CatalystInstanceImpl, 这个是对C++ 层CatalystInstanceImpl 的封装,后面还有一些监听加载的逻辑。CatalystInstanceImpl 里面都是C++ 代码,大部分逻辑都是与iOS共用的,参考iOS C++部分的加载逻辑即可。

private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
    FLog.d(ReactConstants.TAG, "ReactInstanceManager.createReactContext()");
    ReactMarker.logMarker(CREATE_REACT_CONTEXT_START, jsExecutor.getName());
    final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);

    NativeModuleCallExceptionHandler exceptionHandler =
        mNativeModuleCallExceptionHandler != null
            ? mNativeModuleCallExceptionHandler
            : mDevSupportManager;
    reactContext.setNativeModuleCallExceptionHandler(exceptionHandler);

    NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);

    CatalystInstanceImpl.Builder catalystInstanceBuilder =
        new CatalystInstanceImpl.Builder()
            .setContext(mApplicationContext)
            .setExceptionReorters(mExceptionReorters)
            .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
            .setJSExecutor(jsExecutor)
            .setRegistry(nativeModuleRegistry)
            .setJSBundleLoader(jsBundleLoader)
            .setNativeModuleCallExceptionHandler(exceptionHandler);

    ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_START);
    // CREATE_CATALYST_INSTANCE_END is in 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);
    }

    reactContext.initializeWithInstance(catalystInstance);

    // TODO(T46487253): Remove after task is closed
    FLog.e(
        ReactConstants.TAG,
        "ReactInstanceManager.createReactContext: mJSIModulePackage "
            + (mJSIModulePackage != null ? "not null" : "null"));

    if (mJSIModulePackage != null) {
      catalystInstance.addJSIModules(
          mJSIModulePackage.getJSIModules(
              reactContext, catalystInstance.getJavaScriptContextHolder()));

      // TODO(T46487253): Remove after task is closed
      FLog.e(
          ReactConstants.TAG,
          "ReactInstanceManager.createReactContext: ReactFeatureFlags.useTurboModules == "
              + (ReactFeatureFlags.useTurboModules == false ? "false" : "true"));

      if (ReactFeatureFlags.useTurboModules) {
        JSIModule turboModuleManager =
            catalystInstance.getJSIModule(JSIModuleType.TurboModuleManager);

        // TODO(T46487253): Remove after task is closed
        FLog.e(
            ReactConstants.TAG,
            "ReactInstanceManager.createReactContext: TurboModuleManager "
                + (turboModuleManager == null ? "not created" : "created"));

        catalystInstance.setTurboModuleManager(turboModuleManager);

        TurboModuleRegistry registry = (TurboModuleRegistry) turboModuleManager;

        // Eagerly initialize TurboModules
        for (String moduleName : registry.getEagerInitModuleNames()) {
          registry.getModule(moduleName);
        }
      }
    }
    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");
    catalystInstance.runJSBundle();
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);

    return reactContext;
  }
  1. setupReactContext等

前面调用createReactContext方法之后,还有一些后续操作,如setupReactContext , 以及RN环境创建的重试,setupReactContext里面执行了诸如catalystInstance初始化,attachRootViewToInstance等方法,为RN代码的执行做好准备。 RN环境创建的重试具体逻辑为: 判断 mPendingReactContextInitParams是否为空,不为空则重新调用RunCreateReactContextOnNewThread(第5步) ,调用成功后再把mPendingReactContextInitParams置空

private void setupReactContext(final ReactApplicationContext reactContext) {
    FLog.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());

      catalystInstance.initialize();

      mDevSupportManager.onNewReactContextCreated(reactContext);
      mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);
      moveReactContextToCurrentLifecycleState();

      ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START);
      for (ReactRoot reactRoot : mAttachedReactRoots) {
        attachRootViewToInstance(reactRoot);
      }
      ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_END);
    }

    // There is a race condition here - `finalListeners` can contain null entries
    // See usage below for more details.
    ReactInstanceEventListener[] listeners =
        new ReactInstanceEventListener[mReactInstanceEventListeners.size()];
    final ReactInstanceEventListener[] finalListeners =
        mReactInstanceEventListeners.toArray(listeners);

    UiThreadUtil.runOnUiThread(
        new Runnable() {
          @Override
          public void run() {
            for (ReactInstanceEventListener listener : finalListeners) {
              // Sometimes this listener is null - probably due to race
              // condition between allocating listeners with a certain
              // size, and getting a `final` version of the array on
              // the following line.
              if (listener != null) {
                listener.onReactContextInitialized(reactContext);
              }
            }
          }
        });
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    ReactMarker.logMarker(SETUP_REACT_CONTEXT_END);
    reactContext.runOnJSQueueThread(
        new Runnable() {
          @Override
          public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
            ReactMarker.logMarker(CHANGE_THREAD_PRIORITY, "js_default");
          }
        });
    reactContext.runOnNativeModulesQueueThread(
        new Runnable() {
          @Override
          public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
          }
        });
  }

你可能感兴趣的:(RN Android端启动过程)