react-native启动流程(android端)

一、前言

       好吧,其实就是想看看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

未完待续......
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容