美文网首页
react-native启动流程(android端)

react-native启动流程(android端)

作者: T_Y_H | 来源:发表于2018-01-30 17:07 被阅读461次

    一、前言

           好吧,其实就是想看看rn在android上是怎么启动的,因为我是个android攻城师,出于对新技术的追求(公司项目需要),我开始了我的rn之旅。我想在搜索这篇文章的你应该也是一个对android有一定基础的同学,也该有一定的基础,这样你才能看下去,若是没有,我不建议你继续阅读下去(那是浪费时间)。

    二、分析前的场景介绍

           为了更好的(没有干扰)的分析启动流程,我们新建一个空白的rn项目,通过react-native init testGradle命令生成一个名为testGradle的新的rn项目,该命令会为按照一定的结构生成一些目录和文件,进入我们熟悉的android>app>src.main.java.com.testgradle目录,我们会看到已经为我们默认生成了一个MainActivity.java和MainApplication.java两个文件,通过文件的后缀我们不难猜想到这两个java类分别继承了Activity和Application这两个类,在这儿我得说一下,rn生成的在android上的应用和原生java写的android应用在本质上没什么区别,因此我可以用分析android应用启动的流程方式来分析rn生成的应用在android上的启动流程。

    三、分析步骤

    1.MainApplication.java

          熟悉android应用启动流程的同学应该知道,android应用在启动(调用生命周期方法onCreate)第一个Activity之前会先创建一个全局唯一的Application对象,关于application对象的创建时机分析,没有在网上找到合适的文章,等我有空再补上吧,这篇文章是关于Android应用程序启动过程源代码分析也提到了application的创建,不过文章比较长,但的确写的很好,大神之做,建议细细品味!

    首先来分析下MainApplication.java这个文件, 它的继承结构如下


    image.png

    在MainApplication对象创建时,创建成员变量mReactNativeHost对象,进而注入了一些配置,主要注入配置如下:

    • getUseDeveloperSupport() 配置是否开启调试
    • getPackages() 配置要加载的模块
    • getJSMainModuleName() 配置js模块的入口文件名
    2.MainActivity.java 进入主要启动流程

          这个activity是第一个启动的activity,我们通过它来分析启动流程

    Step 1.ReactActivityDelegate.loadApk()

          ReactActivityDelegate类的的说明如下

    Delegate class for {@link ReactActivity} and {@link ReactFragmentActivity}. You can subclass this
    to provide custom implementations for e.g. {@link #getReactNativeHost()}, if your Application
    class doesn't implement {@link ReactApplication}.

          英文不太好,就不献丑了,请自行理解。
          ReactActivityDelegate.java的源代码位置为react-native/ReactAndroid/src/main/java/com/facebook/react/ReactActivityDelegate.java

     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);
      }
    

    这一步主要完成了三件事:

    • 创建了一个mReactRootView对象,它是一个ViewGroup的子类
    • mReactRootView.startReactApplication()开启rn初始化,并获得或者创建一个ReactInstanceManager对象
    • 通过 getPlainActivity().setContentView(mReactRootView);为当前它所代理的Activity设置显示的view

    在进行mReactRootView.startReactApplication()之前,我们来分析下该方法的三个参数:

    • ReactInstanceManager ,非常重要的一个对象,用于管理react中的instance;
    • moduleName,我们这是"testGradle",它必须与js模块中通过AppRegistry.registerComponent()方法注入的名称一致;
    • initialProperties,Bundle类型对象,用于传递一些初始化属性值;

    ReactInstanceManager对象在创建时会对一些管理的对象进行默认的初始化,所以它的创建过程很重要,我们先来看看它的创造流程

    Step 2.ReactNativeHost.getReactInstanceManager()

          ReactNativeHost.java的源代码位置为react-native/ReactAndroid/src/main/java/com/facebook/react/ReactNativeHost.java

    //获取单列的ReactInstanceManager对象
      public ReactInstanceManager getReactInstanceManager() {
        if (mReactInstanceManager == null) {
          mReactInstanceManager = createReactInstanceManager();
        }
        return mReactInstanceManager;
      }
    

    ReactInstanceManager对象是全局唯一的

    Step 3.ReactNativeHost.createReactInstanceManager()
    //用Builder模式创建一个ReactInstanceManager对象
     protected ReactInstanceManager createReactInstanceManager() {
        ReactInstanceManagerBuilder builder = ReactInstanceManager.builder()
          .setApplication(mApplication)//设置Application对象,这儿就是MainApplication对象
          .setJSMainModulePath(getJSMainModuleName())//设置js模块的入口文件名
          .setUseDeveloperSupport(getUseDeveloperSupport())//设置是否支持调试
          .setRedBoxHandler(getRedBoxHandler())//设置
          .setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
          .setUIImplementationProvider(getUIImplementationProvider())
          .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);//设置当前最开始的生命周期状态
    
        for (ReactPackage reactPackage : getPackages()) {//收集所有模块
          builder.addPackage(reactPackage);
        }
    
        String jsBundleFile = getJSBundleFile();//获取要加载的JSBundleFile文件的路径,这个方法是热更新的关键方法
        if (jsBundleFile != null) {
          builder.setJSBundleFile(jsBundleFile);//从自定义路径获取JSBundleFile文件
        } else {
          //加载默认路径(android项目assets目录)下的文件名为"index.android.bundle"的JSBundleFile文件,若是文件不存在,程序直接抛出error并退出。
          builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
        }
        return builder.build();
      }
    

    上面代码主要做了两件事:

    • 创建一个ReactInstanceManagerBuilder对象,并通过该对象进行一些参数设置
    • 调用ReactInstanceManagerBuilder对象的build()方法创造ReactInstanceManager对象

    我们接下里继续分析ReactInstanceManagerBuilder的build()方法

    step 4.ReactInstanceManagerBuilder.build()

          ReactRootView.java源代码位于react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManagerBuilder.java

    public ReactInstanceManager build() {
    
        Assertions.assertNotNull(
          mApplication,
          "Application property has not been set with this builder");
    
        Assertions.assertCondition(
          mUseDeveloperSupport || mJSBundleAssetUrl != null || mJSBundleLoader != null,
          "JS Bundle File or Asset URL has to be provided when dev support is disabled");
    
        Assertions.assertCondition(
          mJSMainModulePath != null || mJSBundleAssetUrl != null || mJSBundleLoader != null,
          "Either MainModulePath or JS Bundle File needs to be provided");
    
        if (mUIImplementationProvider == null) {
          // create default UIImplementationProvider if the provided one is null.
          mUIImplementationProvider = new UIImplementationProvider();
        }
    
        // We use the name of the device and the app for debugging & metrics
        String appName = mApplication.getPackageName();
        String deviceName = getFriendlyDeviceName();
    
        return new ReactInstanceManager(
            mApplication,//就是MainApplication
            mCurrentActivity,//就是MainaActivity
            mDefaultHardwareBackBtnHandler,//物理返回键的处理类,这儿是null
            mJavaScriptExecutorFactory == null//java层js调用执行器,持有一些c++/java的混合对象
                ? new JSCJavaScriptExecutorFactory(appName, deviceName)
                : mJavaScriptExecutorFactory,
            (mJSBundleLoader == null && mJSBundleAssetUrl != null)//JSBundle文件的信息存储类,CatalystInstance用它来加载正确的JSBundle文件
                ? JSBundleLoader.createAssetLoader(
                    mApplication, mJSBundleAssetUrl, false /*Asynchronous*/)
                : mJSBundleLoader,
            mJSMainModulePath,//这儿为默认值"index.android";
            mPackages,//一个Arraylist集合,收集了所有的模块的package
            mUseDeveloperSupport,//是否支持调试
            mBridgeIdleDebugListener,//监听bridge的状态(空闲和忙碌)
            Assertions.assertNotNull(mInitialLifecycleState, "Initial lifecycle state was not set"),//检查生命周期状态是否设置,若没有设置,则给出提示,并中断程序
            mUIImplementationProvider,//
            mNativeModuleCallExceptionHandler,//js调用本地模块时的异常,可以自行定义异常处理方式
            mRedBoxHandler,//一个监听接口,通过它可以在DevSupportManagerImpl中拦截到开发状态下的异常信息
            mLazyNativeModulesEnabled,//
            mLazyViewManagersEnabled,
            mDelayViewManagerClassLoadsEnabled,
            mDevBundleDownloadListener,
            mMinNumShakes,
            mMinTimeLeftInFrameForNonBatchedOperationMs);
      }
    

    上面代码进行主要做了两件事:

    • 创建ReactInstanceManager对象前,检查必要的设置是否已经调用
    • 创建ReactInstanceManager对象,并注入一些对象

    接下来调用了ReactInstanceManager的构造函数进行创建对象,构造函数中又有一些重要对象的初始化,我们来看看

    step 5.ReactInstanceManager构造函数

          ReactRootView.java源代码位于react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java

    /* package */ ReactInstanceManager(
          Context applicationContext,
          @Nullable Activity currentActivity,
          @Nullable DefaultHardwareBackBtnHandler defaultHardwareBackBtnHandler,
          JavaScriptExecutorFactory javaScriptExecutorFactory,
          @Nullable JSBundleLoader bundleLoader,
          @Nullable String jsMainModulePath,
          List<ReactPackage> packages,
          boolean useDeveloperSupport,
          @Nullable NotThreadSafeBridgeIdleDebugListener bridgeIdleDebugListener,
          LifecycleState initialLifecycleState,
          UIImplementationProvider uiImplementationProvider,
          NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler,
          @Nullable RedBoxHandler redBoxHandler,
          boolean lazyNativeModulesEnabled,
          boolean lazyViewManagersEnabled,
          boolean delayViewManagerClassLoadsEnabled,
          @Nullable DevBundleDownloadListener devBundleDownloadListener,
          int minNumShakes,
          int minTimeLeftInFrameForNonBatchedOperationMs) {
        Log.d(ReactConstants.TAG, "ReactInstanceManager.ctor()");
        initializeSoLoaderIfNecessary(applicationContext);
    
        DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(applicationContext);
    
        mApplicationContext = applicationContext;
        mCurrentActivity = currentActivity;
        mDefaultBackButtonImpl = defaultHardwareBackBtnHandler;
        mJavaScriptExecutorFactory = javaScriptExecutorFactory;
        mBundleLoader = bundleLoader;
        mJSMainModulePath = jsMainModulePath;
        mPackages = new ArrayList<>();
        mInitFunctions = new ArrayList<>();
        mUseDeveloperSupport = useDeveloperSupport;
        mDevSupportManager =
            DevSupportManagerFactory.create(
                applicationContext,
                createDevHelperInterface(),
                mJSMainModulePath,
                useDeveloperSupport,
                redBoxHandler,
                devBundleDownloadListener,
                minNumShakes);
        mBridgeIdleDebugListener = bridgeIdleDebugListener;
        mLifecycleState = initialLifecycleState;
        mMemoryPressureRouter = new MemoryPressureRouter(applicationContext);
        mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
        mLazyNativeModulesEnabled = lazyNativeModulesEnabled;
        mDelayViewManagerClassLoadsEnabled = delayViewManagerClassLoadsEnabled;
        synchronized (mPackages) {
          PrinterHolder.getPrinter()
              .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: Use Split Packages");
          mPackages.add(
              new CoreModulesPackage(
                  this,
                  new DefaultHardwareBackBtnHandler() {
                    @Override
                    public void invokeDefaultOnBackPressed() {
                      ReactInstanceManager.this.invokeDefaultOnBackPressed();
                    }
                  },
                  uiImplementationProvider,
                  lazyViewManagersEnabled,
                  minTimeLeftInFrameForNonBatchedOperationMs));
          if (mUseDeveloperSupport) {
            mPackages.add(new DebugCorePackage());
          }
          mPackages.addAll(packages);
        }
    
        // Instantiate ReactChoreographer in UI thread.
        ReactChoreographer.initialize();
        if (mUseDeveloperSupport) {
          mDevSupportManager.startInspector();//处于开发模式,则开启
        }
      }
    

    上面代码主要做了四件事:

    • 将构造函数传入的数据赋值给相应的变量
    • 创建一个mDevSupportManager对象,用于开发模式的交互
    • 创建一个CoreModulesPackage类型对象,封装了对物理返回键的默认处理功能,如果处于开发模式,则加入DebugCorePackage功能模块

    接下来我们继续回到step1中的mReactRootView.startReactApplication()方法

    step 5.ReactRootView.startReactApplication()

          ReactRootView.java源代码位于react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java

     public void startReactApplication(
          ReactInstanceManager reactInstanceManager,
          String moduleName,
          @Nullable Bundle initialProperties) {
          ......
          UiThreadUtil.assertOnUiThread();//判断是否是在ui线程,不是就抛异常,中断程序
          Assertions.assertCondition(
            mReactInstanceManager == null,
            "This root view has already been attached to a catalyst instance manager");
    
          mReactInstanceManager = reactInstanceManager;//持有reactInstanceManager
          mJSModuleName = moduleName;
          mAppProperties = initialProperties;
          
          if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
            mReactInstanceManager.createReactContextInBackground();//初始化ReactContext
          }
    
          attachToReactInstanceManager();//将自己关联到ReactInstanceManager对象上
          ......
      }
    

    上面这步主要完成了三件事:

    • 将传入的三个参数reactInstanceManager、moduleName、initialProperties赋值给了ReactRootView对象
    • 开始初始化话ReactContext
    • 将自己关联到ReactInstanceManager对象上,

    当前场景下mReactInstanceManager.hasStartedCreatingInitialContext()为false,我们进入 mReactInstanceManager.createReactContextInBackground()。

    step 6.ReactInstanceManager.createReactContextInBackground()

          ReactRootView.java源代码位于react-native/ReactAndroid/src/main/java/com/facebook/react/ReactInstanceManager.java

    public void createReactContextInBackground() {
        Log.d(ReactConstants.TAG, "ReactInstanceManager.createReactContextInBackground()");
        //mHasStartedCreatingInitialContext用于标识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();
      }
    

    上面这步做了两件事

    • 判断createReactContextInBackground()是否已经调用过了,是则抛出异常,中断程序
    • 将createReactContextInBackground()方法是已经被调用的flag设置为true,并调用recreateReactContextInBackgroundInner()进行ReactContext创造流程
    step 7.ReactInstanceManager.recreateReactContextInBackgroundInner()
      private void recreateReactContextInBackgroundInner() {
        Log.d(ReactConstants.TAG, "ReactInstanceManager.recreateReactContextInBackgroundInner()");
        PrinterHolder.getPrinter()
            .logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: recreateReactContextInBackground");
        UiThreadUtil.assertOnUiThread();//保证在主线程运行
       
        if (mUseDeveloperSupport
            && mJSMainModulePath != null
            && !Systrace.isTracing(TRACE_TAG_REACT_APPS | TRACE_TAG_REACT_JS_VM_CALLS)) {
          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();
          } else 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();
      }
    

    由于mUseDeveloperSupport为true,mJSMainModulePath 为"index.android",Systrace.isTracing()为false

    未完待续......

    相关文章

      网友评论

          本文标题:react-native启动流程(android端)

          本文链接:https://www.haomeiwen.com/subject/jlofzxtx.html