React Natvie Android 框架启动源码分析


Overview

ReactNative作为一种跨平台开发技术,在Native上下文Context以及运行时Runtime的基础上,启动JavaScript运行时完成布局,再由Native端完成渲染工作。其间涉及了大量的Native <---> JS的通信机制。而ReactNative框架正是基于该通信机制,完成JS与Native组件和接口之间的调用,从而保证了ReactNative业务逻辑高效有序的执行。
整个ReactNative框架的启动和初始化过程,其实就是整个JS与Native之间通信机制建立的过程,本文从ReactAndroid V0.32源码角度对这个过程进行了详细分析,为了便于阅读,我对源码进行了适当的裁剪。
在开始分析之前,首先对于RN框架中的几个重要的核心类进行介绍:

  • ReactRootView是RN界面的最顶层的View,主要负责进行监听分发事件,渲染元素等工作。它可以通过监听View的尺寸从而使UImanager可以对其子元素进行重新布局。
  • ReactInstanceManager(以下简称RIM)主要被用于管理CatalystInstance实例对象。通过ReactPackage,它向开发人员提供了配置和使用CatalystInstance实例对象的方法,并对该catalyst实例的生命周期进行追踪。ReactInstanceManager实例对象的生命周期与ReactRootView所在的activity保持一致。

ReactInstanceManager的创建

ReactNative业务开发中,所有与之相关的Activity均继承自ReactActivity。与其他Android activity一样,当跳转到
ReactNative业务界面后,将回调其onCreate方法,这里主要进行了如下工作:

  1. 创建ReactRootViewReactInstanceManager对象.
  2. 调用startReactApplication方法启动ReactNative的运行时环境.
  3. ReactRootView挂载到当前Activity上.

在启动ReactNative运行时环境之前,首先要创建ReactRootViewReactInstanceManager对象。
ReactRootView对象的创建与普通View的创建并无差异。创建RIM对象时则需要调用ReactNativeHostcreateReactInstanceManager()方法来创建。

根据具体的业务需求,开发者可以在ReactNativeHost中对自定义组件,开发模式,JSBundle路径等进行配置,并利用builder模式完成RIM的创建和初始化工作:

protected ReactInstanceManager createReactInstanceManager() {
    ReactInstanceManager.Builder builder = ReactInstanceManager.builder()
      .setApplication(mApplication)
      .setJSMainModuleName(getJSMainModuleName())
      .setUseDeveloperSupport(getUseDeveloperSupport())
      .setInitialLifecycleState(LifecycleState.BEFORE_CREATE);

    for (ReactPackage reactPackage : getPackages()) {
      builder.addPackage(reactPackage);
    }

    String jsBundleFile = getJSBundleFile();
    if (jsBundleFile != null) {
      builder.setJSBundleFile(jsBundleFile);
    } else {
      builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
    }

    return builder.build();
}

方法中首先对创建ReactInstanceManager的builder进行配置,设置JSmodule入口名称:JSMainModuleName(debug模式有效),是否使用开发者模式:DeveloperSupport,初始化生命周期,并加入封装了自定义组件的package,以及自定义JSBundle文件的路径(如果开发者未指定JSBundle的路径,默认情况下RN则会从Android的asset目录下加载index.android.js文件)。
配置完毕后对ReactInstanceManager进行创建,ReactAndroid0.32中RIM的实现类是XReactInstanceManagerImpl, 除了必要的配置参数外,在其构造函数中还初始化了SoLoaderDevSupportManager

initializeSoLoaderIfNecessary(applicationContext);

mDevSupportManager = DevSupportManagerFactory.create(
        applicationContext,
        mDevInterface,
        mJSMainModuleName,
        useDeveloperSupport,
        redBoxHandler);

ReactActivity将创建好的RIM作为参数传入startReactApplication方法,同时被传入的还有入口模块名称和初始化的组件属性:

  public void startReactApplication(
      ReactInstanceManager reactInstanceManager,
      String moduleName,
      @Nullable Bundle launchOptions) {
    UiThreadUtil.assertOnUiThread();

    mReactInstanceManager = reactInstanceManager;
    mJSModuleName = moduleName;//要启动的JSModule名称
    mLaunchOptions = launchOptions;//启动时传入JS端的可选参数
    //判断当前Activity是否创建过ReactContext
    if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
      mReactInstanceManager.createReactContextInBackground();
    }
    //首次挂载该View时才触发measure过程,只有首次measure后才会将该rootview attach 到RIM上
    if (mWasMeasured) {
      attachToReactInstanceManager();
    }
  }

RN运行时上下文环境ReactContext的创建

从上面代码中可以看到,当Activity首次启动后,通过RIM的createReactContextInBackground方法开始创建ReactNative的上下文环境ReactContextcreateReactContextInBackground方法只会在RN应用启动时候被调用,而主动重新加载JS的时候则是调用方法recreateReactContextInBackground). 在RIM的实现类XReactInstanceManagerImpl中,最终会调用到recreateReactContextInBackgroundInner方法:

private void recreateReactContextInBackgroundInner() {
    UiThreadUtil.assertOnUiThread();

    if (mUseDeveloperSupport && mJSMainModuleName != null) {//Debug模式且设置了入口module的文件名(index.android)
      final DeveloperSettings devSettings = mDevSupportManager.getDevSettings();

      // If remote JS debugging is enabled, load from dev server.
      if (mDevSupportManager.hasUpToDateJSBundleInCache() &&
          !devSettings.isRemoteJSDebugEnabled()) {//cache中有最新的JSbundle,且未开启JS远程Debug
        // 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 DevServerHelper.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;
    }
    //非Debug模式
    recreateReactContextInBackgroundFromBundleLoader();
}

通过对ReactNative所运行的模式进行判断,如果处于开发者模式下,则从Package Server加载JSBundle文件,否则从本地路径下获取:

//Debug模式下从Package Server加载
private void onJSBundleLoadedFromServer() {
    recreateReactContextInBackground(
        new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
        JSBundleLoader.createCachedBundleFromNetworkLoader(
            mDevSupportManager.getSourceUrl(),
            mDevSupportManager.getDownloadedJSBundleFile()));
}
//本地路径加载
private void recreateReactContextInBackgroundFromBundleLoader() {
    recreateReactContextInBackground(
        new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),
        mBundleLoader);
}

从上面代码中可以看到,通过传入recreateReactContextInBackground方法的参数JSBundleLoader的不同从而实现从不同位置获取JS文件。recreateReactContextInBackground方法接受两个参数:

  • JSCJavaScriptExecutor: 位于C++层的JavaScript解析器。当类加载器加载JSCJavaScriptExecutor.class时会加载两个so文件,在它的构造函数里会调用JNI方法来对JSCJavaScriptExecutor进行初始化。
  • JSBundleLoader :被用于存储要加载的JS bundle的信息,帮助catalystInstance可以通过ReactBridge来加载bundle文件。

recreateReactContextInBackground方法中,RIM创建并开始执行异步任务ReactContextInitAsyncTask,该异步任务主要负责在后台完成对RN上下文环境ReactContext进行创建:

private void recreateReactContextInBackground(
      JavaScriptExecutor.Factory jsExecutorFactory,
      JSBundleLoader jsBundleLoader) {
    UiThreadUtil.assertOnUiThread();

    ReactContextInitParams initParams =
        new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);
    if (mReactContextInitAsyncTask == null) {
      // No background task to create react context is currently running, create and execute one.
      mReactContextInitAsyncTask = new ReactContextInitAsyncTask();
      mReactContextInitAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, initParams);
    } else {
      // Background task is currently running, queue up most recent init params to recreate context
      // once task completes.
      mPendingReactContextInitParams = initParams;
    }
}

从上面代码中可以看到,RIM首先为创建ReactContext的初始化参数ReactContextInitParams,然后根据ReactContextInitAsyncTask对象是否存在来判断是否有创建上下文的后台任务在运行。如果当前后台有创建任务正在执行,则将参数入队等待,在当前异步任务执行完毕后再执行。
ReactContextInitAsyncTask负责在AsyncTask中创建react context,每次只能执行一个创建任务。在任务执行前,首先会判断当前是否存在React context,如果有则进行销毁重建,保证同一时刻只有一个React context存在:

//UI线程
protected void onPreExecute() {
      if (mCurrentReactContext != null) {
        tearDownReactContext(mCurrentReactContext);
        mCurrentReactContext = null;
      }
}

异步任务执行过程是在后台线程中进行的,首先会创建出JSExecutor,然后调用createReactContext方法来完成ReactContext的创建工作:

//后台线程
protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {
      Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);

      Assertions.assertCondition(params != null && params.length > 0 && params[0] != null);
      try {
        JavaScriptExecutor jsExecutor = params[0].getJsExecutorFactory().create();
        return Result.of(createReactContext(jsExecutor, params[0].getJsBundleLoader()));
      } catch (Exception e) {
        // Pass exception to onPostExecute() so it can be handled on the main thread
        return Result.of(e);
      }
}

执行完成后重新回到UI线程,将创建好的ReactApplicationContext交给UI线程进行后续的处理。UI线程中的处理工作我们暂且不表,首先看一下异步任务ReactContextInitAsyncTask到底在后台做了哪些工作。
在上面的doInBackground方法中,首先会创建出JSExecutor,然后调用createReactContext方法进行ReactContext的创建工作。在createReactContext方法中主要完成了5项工作:

  1. 处理modules package
  2. 建立Native模块的注册表
  3. 创建catalystInstance实例对象
  4. 创建并初始化ReactApplicationContext
  5. 执行JsBundle
    下面针对这5方面工作进行深入分析。

I. 处理ReactPackage

根据React Native的组件化思想,所有的Native组件和JS组件都要分别继承NativeModuleJavaScriptModule类,并且都需要在CatalystInstance中进行注册。Native组件和JS组件被封装进相应的ReactPackage中:

  • NativeModule: Native Module负责向JS端提供可调用的接口,这些接口由Native端实现。ReactPackage提供了createNativeModules方法来负责创建该ReactPackage中所封装的模块和组件的实例对象。
  • JavaScriptModule:JS Module负责向Native端提供可调用的接口,该接口具体实现由JS端负责。在Native端的JavaScriptModule接口仅仅负责标识该模块是JS module。ReactPackage提供了createJSModules方法来返回该ReactPackage总包含的JS Module的Class类。

CoreModulesPackage定义并组装了核心框架模块(例如UImanager),通常被用于那些需要与框架其他组件集成的模块。
RIM在这里分别对核心packageCoreModulesPackage和用户自定义的ReactPackage进行处理:

//创建Native Module 与JS Module注册表的builder
NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();
.......
//处理RN框架的核心module
CoreModulesPackage coreModulesPackage =
          new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);

.......
for (ReactPackage reactPackage : mPackages) {//处理自定义module
    processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
}

processPackage方法中有两个for循环,要分别对CoreModulesPackage和用户自定义的Native和JS模块进行处理:

private void processPackage(
      ReactPackage reactPackage,
      ReactApplicationContext reactContext,
      NativeModuleRegistry.Builder nativeRegistryBuilder,
      JavaScriptModuleRegistry.Builder jsModulesBuilder) {
    for (NativeModule nativeModule : reactPackage.createNativeModules(reactContext)) {
      nativeRegistryBuilder.add(nativeModule);
    }
    for (Class<? extends JavaScriptModule> jsModuleClass : reactPackage.createJSModules()) {
      jsModulesBuilder.add(jsModuleClass);
    }
}

其处理方式实际上就是创建出Native module的实例对象列表和JS module的Class列表,然后将其传递给各自注册表的builder备用。在NativeRegistryBuilder的add方法中:

public Builder add(NativeModule module) {
      NativeModule existing = mModules.get(module.getName());
      if (existing != null && !module.canOverrideExistingModule()) {
        throw new IllegalStateException("... ");
      }
      mModules.put(module.getName(), module);
      return this;
}

NativeRegistryBuilder使用HashMap<ModuleName, NativeModule>来存放module信息,而JavaScriptModuleRegistryBuilder则使用了List<JavaScriptModuleRegistration>,其中JavaScriptModuleRegistration只是一个封装了JSmodule Class的对象。

II. 模块的注册工作

利用builder,RIM分别建立Native模块与JS模块的注册表NativeModulesRegistryJavaScriptModuleRegistry。首先来看NativeModulesRegistry的创建:

public NativeModuleRegistry build() {
      Map<Class<NativeModule>, NativeModule> moduleInstances = new HashMap<>();
      for (NativeModule module : mModules.values()) {
        moduleInstances.put((Class<NativeModule>)module.getClass(), module);
      }
      return new NativeModuleRegistry(moduleInstances);
}

可以看到,nativeRegistryBuilder将之前存于HashMap<ModuleName,NativeModule>中的Native模块取出,重新封装进HashMap<ClassName,NativeModule>中后传递给NativeModuleRegistry的构造方法:

private NativeModuleRegistry(Map<Class<NativeModule>, NativeModule> moduleInstances) {
    mModuleInstances = moduleInstances;
    mBatchCompleteListenerModules = new ArrayList<OnBatchCompleteListener>(mModuleInstances.size());
    for (NativeModule module : mModuleInstances.values()) {
      if (module instanceof OnBatchCompleteListener) {
        mBatchCompleteListenerModules.add((OnBatchCompleteListener) module);
      }
    }
}

构造方法中,取出那些JS调用Native层方法结束后需要接收通知的module封装进一个List中。
同样,JavaScriptModuleRegistry.Builder也根据JS模块的注册信息创建出JS注册表JavaScriptModuleRegistry

public JavaScriptModuleRegistry(List<JavaScriptModuleRegistration> config) {
    mModuleInstances = new WeakHashMap<>();
    mModuleRegistrations = new HashMap<>();
    for (JavaScriptModuleRegistration registration : config) {
      mModuleRegistrations.put(registration.getModuleInterface(), registration);
    }
}

JavaScriptModuleRegistry首先创建了一个WeakHashMap和一个HashMap<Class<? extends JavaScriptModule>, JavaScriptModuleRegistration>。然后对后者进行了初始化,实际上就是把JSModule的Class对象和封装该类对象的JavaScriptModuleRegistration分别作为<K,V>放入HashMap中。

III. 创建并初始化catalystInstance实例对象

CatalystInstance是一个封装了异步JSC的顶级API,它提供了JS和Java之间交互的环境。CatalystInstance的实现类为CatalystInstanceImpl
RIM对CatalystInstanceImpl对象进行了封装,从而帮助开发者通过RIM对它进行操作。CatalystInstance初始化时构建了ReactBridge,从而通过所持有的ReactBridge(JNI)来实现Native和JS的通信工作。
在创建catalystInstance的实例对象时,同样是利用builder模式。首先构建builder:

CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
        .setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
        .setJSExecutor(jsExecutor)
        .setRegistry(nativeModuleRegistry)
        .setJSModuleRegistry(jsModulesBuilder.build())
        .setJSBundleLoader(jsBundleLoader)
        .setNativeModuleCallExceptionHandler(exceptionHandler);

在builder中,我们可以为CatalystInstance设置以下组件:

  • ReactQueueConfigurationSpec:该类包含了创建的消息队列的配置信息ReactQueueConfiguration的参数;
  • NativeModuleRegistryJSmoduleRegistry:Native模块与JS模块的注册表;
  • JSBundleLoader:该类存储了JSbundle的信息,保证CatalystInstance通过ReactBridge加载正确的bundle;
  • NativeModuleCallExceptionHandler负责处理JS调用native接口所引发的异常;

CatalystInstanceImpl的构造方法中,首先根据之前传入的ReactQueueConfigurationSpec参数创建了一个ReactQueueConfiguration对象:

mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
        ReactQueueConfigurationSpec,
        new NativeExceptionHandler());

这个类中封装了RN框架中通信的三个消息队列线程:

MessageQueueThread getUIQueueThread();
MessageQueueThread getNativeModulesQueueThread();
MessageQueueThread getJSQueueThread();

MessageQueueThread中封装了一个线程,该线程持有一个looper,可以接受Runnable/Callable对象来执行。
除了创建消息队列线程,还将调用JNI方法初始化Hybrid数据,以及完成对ReactBridge的初始化工作。
之前对于ReactBridge的初始化,RN在Java层进行了实现,而在0.32之后的版本中则选择使用JNI调用Cpp的代码来实现。

IV. 创建并初始化ReactApplicationContext

ReactApplicationContext是一个Context Wapper,里面封装了Android的Application Context以及CatalystInstance对象,以及RN框架处理消息的三个线程:

final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);

创建好的catalystInstance对象将通过所持有的JS线程执行ReactContext的初始化工作:

catalystInstance.getReactQueueConfiguration().getJSQueueThread().callOnQueue(
        new Callable<Void>() {
          @Override
          public Void call() throws Exception {
            reactContext.initializeWithInstance(catalystInstance);

            
            try {
              catalystInstance.runJSBundle();
            }
            return null;
          }
        }).get();

ReactContext的初始化工作实际上就是把创建好的catalystInstance对象以及该对象所持有的三个消息队列线程传递给ReactContext

V. 运行JS Bundle

异步任务的最后一项工作是在JS线程中通过调用catalystInstance对象的runJSBundle方法,对JS bundle进行加载和解析:

public void runJSBundle() {
    mAcceptCalls = true;
    mJSBundleHasLoaded = true;
    mJSBundleLoader.loadScript(CatalystInstanceImpl.this);
}

JSBundleLoader最终会调用到CatalystInstanceImpl的JNI方法来加载JSBundle文件,这个过程是异步的。
ReactContext创建和初始化完毕后,后台执行的异步任务终于结束了。
ReactContextInitAsyncTaskonPostExecute方法将在主线程中回调,负责进行后续的处理工作。为了便于理解,我们假定异步任务在后台执行期间,ReactActivity中setContentView方法还没有执行完毕,也就是说RootView的尺寸大小还未被测量。
通常情况下,异步任务的回调方法通常会在主线程空闲后才执行,这里为了衔接方便,分析了onPostExecute方法立即被回调的情况。
异步任务的onPostExecute方法进行收尾工作。首先由RIM调用setupReactContext方法启动刚刚创建好的ReactContext

private void setupReactContext(ReactApplicationContext reactContext) {

    UiThreadUtil.assertOnUiThread();

    mCurrentReactContext = Assertions.assertNotNull(reactContext);
    CatalystInstance catalystInstance =
        Assertions.assertNotNull(reactContext.getCatalystInstance());

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

    for (ReactRootView rootView : mAttachedRootViews) {
      attachMeasuredRootViewToInstance(rootView, catalystInstance);
    }

.......


  }

setupReactContext方法中,RIM首先将调用catalystInstanceinitialize()方法对Native模块进行初始化:

 public void initialize() {
    UiThreadUtil.assertOnUiThread();
    mInitialized = true;
    mJavaRegistry.notifyCatalystInstanceInitialized();
  }

在UI线程中,notifyCatalystInstanceInitialized方法调用各自的initialize方法,从而完成Native模块的初始化工作。

  /* package */ void notifyCatalystInstanceInitialized() {
    UiThreadUtil.assertOnUiThread();
    try {
      for (NativeModule nativeModule : mModuleInstances.values()) {
        nativeModule.initialize();
      }
    }
  }

RIM对象维护了一个列表来存储与之关联的ReactRootView。由于我们假定当前RootView的尺寸仍未被测量,因此还未被添加进该列表中,关联方法attachMeasuredRootViewToInstance也不会被执行。
setupReactContext执行完毕后,当前ReactContextInitAsyncTask异步任务实例也被销毁掉,如果队列中没有新的异步任务请求,那么异步任务的收尾工作就此完成。


启动JS Application

在异步任务创建上下文环境ReactContext的同时,ReactActivity在主线程中对ReactRootView进行测量,回调其onMeasure方法:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    .....
    mWasMeasured = true;
    // Check if we were waiting for onMeasure to attach the root view
    if (mReactInstanceManager != null && !mIsAttachedToInstance) {
      // Enqueue it to UIThread not to block onMeasure waiting for the catalyst instance creation
      UiThreadUtil.runOnUiThread(new Runnable() {
        @Override
        public void run() {
          attachToReactInstanceManager();
        }
      });
    }
}

测量完成后,判断RIM创建成功,ReactRootView最终调用了RIM的attachMeasuredRootView方法,将自己和刚刚创建好的RIM实例对象关联起来:

public void attachMeasuredRootView(ReactRootView rootView) {
    UiThreadUtil.assertOnUiThread();
    mAttachedRootViews.add(rootView);

    if (mReactContextInitAsyncTask == null && mCurrentReactContext != null) {
      attachMeasuredRootViewToInstance(rootView, mCurrentReactContext.getCatalystInstance());
    }
}

RIM对象维护了一个列表来存储与之关联的ReactRootView。RIM把当前RootView添加至该列表后,如果在后台线程中进行的异步任务已经将上下文环境ReactContext创建完毕,则将其与对应的CatalystInstance相关联:

private void attachMeasuredRootViewToInstance(
      ReactRootView rootView,
      CatalystInstance catalystInstance) {
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "attachMeasuredRootViewToInstance");
    UiThreadUtil.assertOnUiThread();

    rootView.removeAllViews();
    rootView.setId(View.NO_ID);

    UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);
    int rootTag = uiManagerModule.addMeasuredRootView(rootView);
    rootView.setRootViewTag(rootTag);
   
    ......
}

RIM首先重置了该RootView下的所有内容(删除所有子View),其中的内容随后将被JS提供的界面所填充。
接下来,RIM获取到Native模块UIManagerModule,并调用其addMeasuredRootView方法,把RootView注册到该模块。

UIManagerModule作为一个Native module主要负责JS创建和更新Native View。

addMeasuredRootView方法中,UIManagerModule首先为RootView生成了一个Tag值,可以被JS端所用于管理其中子View:

public int addMeasuredRootView(final SizeMonitoringFrameLayout rootView) {
    final int tag = mNextRootViewTag;
    mNextRootViewTag += ROOT_VIEW_TAG_INCREMENT;

    final int width;
    final int height;

    if (rootView.getLayoutParams() != null &&
        rootView.getLayoutParams().width > 0 &&
        rootView.getLayoutParams().height > 0) {
      width = rootView.getLayoutParams().width;
      height = rootView.getLayoutParams().height;
    } else {
      width = rootView.getWidth();
      height = rootView.getHeight();
    }

    final ThemedReactContext themedRootContext =
        new ThemedReactContext(getReactApplicationContext(), rootView.getContext());

    mUIImplementation.registerRootView(rootView, tag, width, height, themedRootContext);

    rootView.setOnSizeChangedListener(
      new SizeMonitoringFrameLayout.OnSizeChangedListener() {
        @Override
        public void onSizeChanged(final int width, final int height, int oldW, int oldH) {
          getReactApplicationContext().runOnNativeModulesQueueThread(
            new Runnable() {
              @Override
              public void run() {
                updateRootNodeSize(tag, width, height);
              }
            });
        }
      });

    return tag;
}

在获取到RootView的尺寸后,将其注册给UIImplementation.

UIImplementation负责接收来自JS端的调用命令并将其转换为shadow node层级,随后被映射为真实的Native View层级关系

回到RIM的attachMeasuredRootViewToInstance方法中,RIM获取到JSMoudleAppRegistry,设置好初始化参数后,调用其方法runApplication启动JS application。

@Nullable Bundle launchOptions = rootView.getLaunchOptions();
    WritableMap initialProps = Arguments.makeNativeMap(launchOptions);
    String jsAppModuleName = rootView.getJSModuleName();

    WritableNativeMap appParams = new WritableNativeMap();
    appParams.putDouble("rootTag", rootTag);
    appParams.putMap("initialProps", initialProps);
    catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);

此时,RN的上下文环境ReactContext以及随后的收尾工作基本完成。


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

推荐阅读更多精彩内容