react native 代码执行(一)

react native Android端代码执行的大体流程:

第一步:在应用的Application里做RN的初始化操作。

有两个需要注意的对象:

  • ReactNativeHost:持有ReactInstanceManager实例,做一些初始化操作。
  • 重载方法onCreate里面的SoLoader:加载C++底层库,准备解析JS。

第二步:页面继承ReactActivityReactActivity作为JS页面的容器。

第三步:有了ReactActivity作为容器,我们就可以用JS开发页面了。

react native启动流程相关的一些重要概念

ReactContext

概念:

ReactContext继承于ContextWrapper,也就是说它和Android中的Context是一个概念,是整个应用的上下文。那么什么是上下文呢,我们知道Android的应用模型是基于组件的应用设计模式, 组件的运行需要完整的运行环境,这种运行环境便是应用的上下文。

NativeModule/UIManagerModule/JavascriptModule

NativeModule/UIManagerModuleNativeModule是Java暴露给JS调用的APU集合,UIManagerModule也是供JS调用的API集合,它用来创建View。业务放可以通过实现NativeModule来自定义模块,通过getName()将模块名暴露给JS层,通过@ReactMethod注解将API暴露给JS层。

JavaScriptModule:JS暴露给Java调用的API集合等。业务放可以通过继承JavaScriptModule接口类似自定义接口模块,声明与JS相对应的方法
即可。

一、执行器的实现

1.1 Native代码执行器

ExecutorDelegate:在Executor.h中定义,由JsToNativeBridge实现,该抽象类用于JS代码调用Native代码。

1.2 JS代码执行器

JS的解析是在Webkit-JavaScriptCore中完成的,JSCExexutor.cpp对JavaScriptCore的功能做了进一步的封装,我们来看一下它的实现。

JSExecutor:在Executor.h中定义,正如它的名字那样,它是用来执行JS代码的。执行代码的命令是通过JS层的BatchedBridge传递过来的。

RN给了我们自定义JS解析器的能力,可以在CatalystInstance.BuildersetJSExecutor()

二、RN应用的启动流程

一句话概括启动流程:先是应用终端启动并创建应用上下文,应用上下文启动JS Runtime,进行布局,再由应用终端进行渲染,最后将渲染的View添加到ReactRootView上,最终呈现在用户面前。

详细流程:

  1. 在程序启动的时候,也就是ReContextactActivityonCreate()函数中,我们会去创建一个ReactInstanceManagerImpl对象。
  2. ReactRootView作为整个RN应用的根视图,通过调用ReactRootView.startReactApplication()方法启动RN应用。
  3. RN应用页面渲染前,需要先创建ReactContext的创建流程,异步任务ReactContextInitAsyncTask负责来完成这个任务。
  4. ReactContextInitAsyncTask在后台ReactContextInitAsyncTask.doInBackground()执行ReactContext的创建,创建ReactContext的过程中,会依据ReactPackage创建JavaScriptModuleRegistryNativeModuleRegistry注册表以及它们的管理类CatalystInstanceImpl,同时创建JS、Native与UI线程队列,并最终调用CatalystInstanceImpl.runJSBundle()去异步加载JS Bundle文件。
  5. 后台任务执行完成后,在ReactContextInitAsyncTask.onPostExecute()会调用ReactInstanceManager.setupReactContext()设置创建好的ReactContext,并将ReactRootView加载进来,并调用RN应用的JS入口APPRegistry来启动应用。
  6. JS层找到已经注册的对应的启动组件,执行renderApplication()来渲染整个应用。

接下来根据关键的方法进行更详细的分析:

1.ReactActivityDelegate.onCreate(Bundle savedInstanceState)

ReactActivityDelegate在创建时主要做了以下事情:

  1. 创建ReactRootView作为应用的容器,它本质上是一个FrameLayout
  2. 调用ReactRootView.startReactApplication()进一步执行应用启动流程。
  3. 调用Activity.setContentView()将创建的ReactRootView作为ReactActivity的content view。

可以看出RN真正核心的地方就在于ReactRootView,它就是一个View,你可以像用其他UI组件那样把它用在Android应用的任何地方。好,我们进一步去ReactRootView看启动流程。

2.ReactRootView.startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions)

我们来看看这个函数的3个参数:

  • ReactInstanceManager reactInstanceManager:管理React实例。
  • String moduleName:模块的名字,对应ReactActivity.getMainComponentName()与AppRegistry.registerComponent()。
  • Bundle launchOptions:Bundle类型的数据,如果我们不继承ReactActivity而是自己实现页面容器,可以通过这个参数在startActivity()时传递参数到JS层。

我们可以看到,ReactRootView.startReactApplication()方法里最终会调用ReactInstanceManager.createReactContextInBackground()来创建RN应用的上下文。

3.ReactInstanceManager.createReactContextInBackground()

整个代码的调用链,最终开启异步任务ReactContextInitAsyncTask来创建上下文ReactApplicationContext

ReactInstanceManager.createReactContextInBackground()
->ReactInstanceManager.recreateReactContextInBackground() ->ReactInstanceManager.recreateReactContextInBackgroundInner() ->ReactInstanceManager.recreateReactContextInBackgroundFromBundleLoader() ->ReactInstanceManager.recreateReactContextInBackground(JavaScriptExecutor.Factory jsExecutorFactory, JSBundleLoader jsBundleLoader) ->ReactContextInitAsyncTask

该方法启动了一个ReactContextInitAsyncTask的异步任务栈。

4.ReactInstanceManager.ReactContextInitAsyncTask.doInBackground(ReactContextInitParams… params)

ReactContextInitAsyncTask的doInBackground()方法里调用ReactInstanceManager.createReactContext()最终执行了ReactApplicationContext的创建。
我们重点来看看传入ReactInstanceManager.createReactContext()的2个参数:

  • JSCJavaScriptExecutor jsExecutorJSCJavaScriptExecutor继承于JavaScriptExecutor,当该类被加载时,它会自动去加载”reactnativejnifb.so”库,并会调用Native方法initHybrid()初始化C++层RN与JSC通信的框架。
  • JSBundleLoader jsBundleLoader:缓存了JSBundle的信息,封装了上层加载JSBundle的相关接口,CatalystInstance通过其简介调用ReactBridge去加载JS文件,不同的场景会创建不同的加载器,具体可以查看类JSBundleLoader

这两个参数ReactInstanceManager.recreateReactContextInBackground()创建ReactContextInitAsyncTask传递进来的,有两个地方调用了ReactInstanceManager.recreateReactContextInBackground() 方法,

接下来调用ReactInstanceManager.createReactContext(),真正开始创建ReactContext

2.5 ReactInstanceManager.createReactContext(JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader)

这个方法,主要做了以下事情:

  1. 创建JavaModule注册表与JavaScriptModule注册表,这两张表最后都交由CatalystInstance管理。
  2. 处理ReactPackage,将JavaModuleJavaScriptModule放进各自对应的注册表里。
  3. 通过上面jsExecutornativeModuleRegistryjsModulesRegistryjsBundleLoaderexceptionHandler等参数创建CatalystInstance实例。
  4. 关联ReactContextCatalystInstance,并将JS Bundle加载进来,等待ReactContextInitAsyncTask结束以后调用JS入口渲染页面。

2.6 CatalystInstanceImpl.CatalystInstanceImpl( final ReactQueueConfigurationSpec ReactQueueConfigurationSpec, final JavaScriptExecutor jsExecutor, final NativeModuleRegistry registry, final JavaScriptModuleRegistry jsModuleRegistry, final JSBundleLoader jsBundleLoader, NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler)

这个方法的参数特别多,从CatalystInstanceImpl的构建过程可以看出,CatalystInstanceImpl是个封装管理类,封装了各种注册表,以及初始化JNI,我们来看看最后初始化Bridge传入的6个参数:

  • ReactCallback callbackCatalystInstanceImpl的静态内部类ReactCallback,负责接口回调。
  • JavaScriptExecutor jsExecutor:JS执行器,将JS的调用传递给C++层。
  • MessageQueueThread jsQueue.getJSQueueThread():JS线程,通过mReactQueueConfiguration.getJSQueueThread()获得,mReactQueueConfiguration通过ReactQueueConfigurationSpec.createDefault()创建。
  • MessageQueueThread moduleQueueNative线程,通过mReactQueueConfiguration.getNativeModulesQueueThread()获得,mReactQueueConfiguration通过ReactQueueConfigurationSpec.createDefault()创建。
  • Collection<JavaModuleWrapper> javaModulesjava modules,来源于mJavaRegistry.getJavaModules(this)
  • Collection<ModuleHolder> cxxModules:c++ modules,来源于mJavaRegistry.getCxxModules()

CatalystInstanceImpl被创建以后,便进行JS的加载。从上面第5步:ReactInstanceManager.createReactContext()方法可以知道,该函数会调
CatalystInstanceImpl.runJSBundle()来加载JS Bundle。我们开看一下它的实现。

2.7 CatalystInstanceImpl.runJSBundle()

CatalystInstanceImpl.runJSBundle()会调用JSBundleLoader去加载JS Bundle,由于不同的情况可能会有不同的JSBundleLoaderJSBundleLoader会继续调用CatalystInstanceImpl.loadScriptFromAssets()方法去加载JS Bundle

2.8 CatalystInstanceImpl.loadScriptFromAssets(AssetManager assetManager, String assetURL)

CatalystInstanceImpl.java最终还是调用C++层的CatalystInstanceImpl.cpp去加载JS Bundle

猜你喜欢

转载自blog.csdn.net/r122555/article/details/81056653