react native Android端代码执行的大体流程:
第一步:在应用的Application
里做RN的初始化操作。
有两个需要注意的对象:
ReactNativeHost
:持有ReactInstanceManager
实例,做一些初始化操作。- 重载方法
onCreate
里面的SoLoader
:加载C++底层库,准备解析JS。
第二步:页面继承ReactActivity
,ReactActivity
作为JS页面的容器。
第三步:有了ReactActivity
作为容器,我们就可以用JS开发页面了。
react native启动流程相关的一些重要概念
ReactContext
概念:
ReactContext
继承于ContextWrapper
,也就是说它和Android
中的Context
是一个概念,是整个应用的上下文。那么什么是上下文呢,我们知道Android
的应用模型是基于组件的应用设计模式, 组件的运行需要完整的运行环境,这种运行环境便是应用的上下文。
NativeModule/UIManagerModule/JavascriptModule
NativeModule
/UIManagerModule
:NativeModule
是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.Builder
里 setJSExecutor()
。
二、RN应用的启动流程
一句话概括启动流程:先是应用终端启动并创建应用上下文,应用上下文启动JS Runtime
,进行布局,再由应用终端进行渲染,最后将渲染的View
添加到ReactRootView上
,最终呈现在用户面前。
详细流程:
- 在程序启动的时候,也就是
ReContextactActivity
的onCreate()
函数中,我们会去创建一个ReactInstanceManagerImpl
对象。 ReactRootView
作为整个RN应用的根视图,通过调用ReactRootView.startReactApplication()
方法启动RN应用。- RN应用页面渲染前,需要先创建
ReactContext
的创建流程,异步任务ReactContextInitAsyncTask
负责来完成这个任务。 ReactContextInitAsyncTask
在后台ReactContextInitAsyncTask.doInBackground()
执行ReactContext
的创建,创建ReactContext
的过程中,会依据ReactPackage
创建JavaScriptModuleRegistry
与NativeModuleRegistry
注册表以及它们的管理类CatalystInstanceImpl
,同时创建JS、Native与UI线程队列,并最终调用CatalystInstanceImpl.runJSBundle()
去异步加载JS Bundle
文件。- 后台任务执行完成后,在
ReactContextInitAsyncTask.onPostExecute()
会调用ReactInstanceManager.setupReactContext()
设置创建好的ReactContext
,并将ReactRootView
加载进来,并调用RN应用的JS入口APPRegistry
来启动应用。 - JS层找到已经注册的对应的启动组件,执行
renderApplication()
来渲染整个应用。
接下来根据关键的方法进行更详细的分析:
1.ReactActivityDelegate.onCreate(Bundle savedInstanceState)
ReactActivityDelegate
在创建时主要做了以下事情:
- 创建
ReactRootView
作为应用的容器,它本质上是一个FrameLayout
。 - 调用
ReactRootView.startReactApplication()
进一步执行应用启动流程。 - 调用
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
jsExecutor
:JSCJavaScriptExecutor
继承于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)
这个方法,主要做了以下事情:
- 创建
JavaModule
注册表与JavaScriptModule
注册表,这两张表最后都交由CatalystInstance
管理。 - 处理
ReactPackage
,将JavaModule
与JavaScriptModule
放进各自对应的注册表里。 - 通过上面
jsExecutor
、nativeModuleRegistry
、jsModulesRegistry
、jsBundleLoader
、exceptionHandler
等参数创建CatalystInstance
实例。 - 关联
ReactContext
与CatalystInstance
,并将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
callback
:CatalystInstanceImpl
的静态内部类ReactCallback
,负责接口回调。JavaScriptExecutor
jsExecutor
:JS执行器,将JS的调用传递给C++层。MessageQueueThread
jsQueue.getJSQueueThread()
:JS线程,通过mReactQueueConfiguration.getJSQueueThread()
获得,mReactQueueConfiguration
通过ReactQueueConfigurationSpec.createDefault()
创建。MessageQueueThread
moduleQueue
:Native
线程,通过mReactQueueConfiguration.getNativeModulesQueueThread()
获得,mReactQueueConfiguration
通过ReactQueueConfigurationSpec.createDefault()
创建。Collection<JavaModuleWrapper>
javaModules
:java 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
,由于不同的情况可能会有不同的JSBundleLoader
。JSBundleLoader
会继续调用CatalystInstanceImpl.loadScriptFromAssets()
方法去加载JS Bundle
。
2.8 CatalystInstanceImpl.loadScriptFromAssets(AssetManager assetManager, String assetURL)
CatalystInstanceImpl.java
最终还是调用C++层的CatalystInstanceImpl.cpp
去加载JS Bundle
。