写点什么

React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信)

用户头像
Android架构
关注
发布于: 2021 年 11 月 05 日

1. reactInstanceManager: 大内总管接口类,提供一个构造者模式的初始化 Builder,实现类是 XReactInstanceManagerImpl,这类也是我们在集成 RN 时 new ReactRootView 的之前自己创建的。


2. moduleName: 与 JS 代码约定的 String 类型识别 name,JS 端通过 AppRegistry.registerComponent 方法设置这个 name,Java 端重写基类的 getMainComponentName 方法设置这个 name,这样两边入口就对上了。


3. launchOptions: 这里默认是 null 的,如果自己不继承 ReactActivity 而自己实现的话可以通过这个参数在 startActivity 时传入一些参数到 JS 代码,用来依据参数初始化 JS 端代码。


这些参数都初始化传递好了以后,可以看见接着调用了 mReactInstanceManager 的 createReactContextInBackground 方法,mReactInstanceManager 就是上面说的第一个参数,实质是通过一个构造者模式创建的,实现类是 XReactInstanceManagerImpl,所以我们直接跳到 XReactInstanceManagerImpl 的 createReactContextInBackground 方法看看,如下:


public void createReactContextInBackground() {


......


recreateReactContextInBackgroundInner();


}


private void recreateReactContextInBackgroundInner() {


UiThreadUtil.assertOnUiThread();


if (mUseDeveloperSupport && mJSMainModuleName != null) {


//如果是 dev 模式,BuildConfig.DEBUG=true 就走这里,在线更新 bundle,手机晃动出现调试菜单等等。


//这个路线属于 RN 调试流程原理,后面再写文章分析,这里我们抓住主线分析


......


return;


}


//非调试模式,即 BuildConfig.DEBUG=false 时执行


recreateReactContextInBackgroundFromBundleLoader();


}


private void recreateReactContextInBackgroundFromBundleLoader() {


//厉害了,word 哥,在后台创建 ReactContext,两个参数是重点。


//mJSCConfig.getConfigMap()默认是一个 WritableNativeMap,在前面通过构造模式构造时通过 Builder 类的 set 方法设置。


//mJSBundleLoader 是在前面通过构造模式构造时通过 Builder 类的多个 setXXX 方法均可设置的,


//最终在 Builder 中 build 方法进行判断处理,你可以自定义 Loader,或者按照 build 方法规则即可,


//默认是 JSBundleLoader.createAssetLoader 静态方法返回的 JSBundleLoader 抽象类的实现类。


//自定义热更新时 setJSBundleFile 方法参数就是巧妙的利用这里是走 JSBundleLoader.createAssetLoader 还是 JSBundleLoader.createFileLoader!!!!!!


recreateReactContextInBackground(


new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()),


mBundleLoader);


}


private void recreateReactContextInBackground(


JavaScriptExecutor.Factory jsExecutorFactory,


JSBundleLoader jsBundleLoader) {


UiThreadUtil.assertOnUiThread();


//封装一把,把两个参数封装成 ReactContextInitParams 对象


ReactContextInitParams initParams =


new ReactContextInitParams(jsExecutorFactory, jsBundleLoader);


if (mReactContextInitAsyncTask == null) {


//初始化进来一定会走啦,这货不就是创建一个 AsyncTask,然后执行,同时传递封装的参数 initParams 给 task。


// 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;


}


}


通过上面注释可以看见,我们[《React Native Android 从学车到补胎和成功发车经历 #3-5 RN 集成后热更新核心思路》](


)的热更新面纱也是从这个地方开始揭晓的,ReactInstanceManager 的 setJSBundleFile 如下:


/**


  • Path to the JS bundle file to be loaded from the file system.

  • Example: {@code "assets://index.android.js" or "/sdcard/main.jsbundle"}


*/


public Builder setJSBundleFile(String jsBundleFile) {


if (jsBundleFile.startsWith("assets://")) {


mJSBundleAssetUrl = jsBundleFile;


mJSBundleLoader = null;


return this;


}


return setJSBundleLoader(JSBundleLoader.createFileLoader(jsBundleFile));


}


我们先记住这个提示,后面再边分析主加载流程边插入介绍热更新的原理,所以我们还是把思路先回到 XReactInstanceManagerImpl 内部类的 ReactContextInitAsyncTask 上,如下:


private final class ReactContextInitAsyncTask extends


AsyncTask<ReactContextInitParams, Void, Result<ReactApplicationContext>> {


......


@Override


protected Result<ReactApplicationContext> doInBackground(ReactContextInitParams... params) {


......


try {


//异步执行的重量级核心方法 createReactContext,创建 ReactContext


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


}


}


@Override


protected void onPostExecute(Result<ReactApplicationContext> result) {


try {


//回到主线程执行的重量级核心方法 setupReactContext,设置 ReactContext 相关


setupReactContext(result.get());


} catch (Exception e) {


mDevSupportManager.handleException(e);


} finally {


mReactContextInitAsyncTask = null;


}


......


}


......


}


可以看见,这就是典型的 AsyncTask 用法,我们先关注 doInBackground 方法,onPostExecute 方法等会回头再看;doInBackground 中首先把上面封装的 ReactContextInitParams 对象里 JavaScriptExecutor.Factory 工厂对象拿到,接着调用了工厂类的 create 方法创建 JavaScriptExecutor 抽象类的实现类 JSCJavaScriptExecutor 对象(因为上面分析 recreateReactContextInBackground 方法时第一个参数传入的是 new JSCJavaScriptExecutor.Factory(mJSCConfig.getConfigMap()))。接着往下执行了 createReactContext 方法,两个参数分别是前面封装的 ReactContextInitParams 对象中的 JSCJavaScriptExecutor 实例和 JSBundleLoader.createAssetLoader 静态方法创建的匿名内部类 JSBundleLoader 对象(热更新的话可能是另一个 Loader,参见前面分析);createReactContext 方法如下(有点长,但是句句核心啊):


private ReactApplicationContext createReactContext(


JavaScriptExecutor jsExecutor,


JSBundleLoader jsBundleLoader) {


......


//这货默认不就是上面刚刚分析的"assets://" + bundleAssetName 么


mSourceUrl = jsBundleLoader.getSourceUrl();


List<ModuleSpec> moduleSpecs = new ArrayList<>();


Map<Class, ReactModuleInfo> reactModuleInfoMap = new HashMap<>();


//!!!Js 层模块注册表,通过它把所有的 JavaScriptModule 注册到 CatalystInstance。我们自定义的继承 JavaScriptModule 接口的 Java 端也是通过他来管理。


JavaScriptModuleRegistry.Builder jsModulesBuilder = new JavaScriptModuleRegistry.Builder();


//ContextWrapper 封装类,其实就是 getApplicationContext 的封装,用在 ReactContext 中


final ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);


//如果是开发模式下 ReactApplicationContext 中有崩溃就捕获后交给 mDevSupportManager 处理(出错时弹个红框啥玩意的都是这货捕获的功劳)


if (mUseDeveloperSupport) {


//mDevSupportManager 实例对象来源于 XReactInstanceManagerImpl 构造方法中一个工厂方法,实质由 useDeveloperSupport 决定 DevSupportManager 是哪个实例。


//非开发模式情况下 mDevSupportManager 为 DisabledDevSupportManager 实例,开发模式下为 DevSupportManagerImpl 实例。


reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);


}


......


try {


//创建 CoreModulesPackage(ReactPackage),RN framework 的核心 Module Package,主要通过 createNativeModules、createJSModules 和 createViewManagers 等方法创建本地模块,JS 模块及视图组件等。


//CoreModulesPackage 封装了通信、调试等核心类。


CoreModulesPackage coreModulesPackage =


new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);


//当我们设置 mLazyNativeModulesEnabled=true(默认 false)后启动可以得到延迟加载,感觉没啥卵用,没整明白有何用意。


//拼装来自 coreModulesPackage 的各种 module 了,JS 的直接 add 进了 jsModulesBuilder 映射表、Native 的直接保存在了 moduleSpecs、reactModuleInfoMap 中。


processPackage(


coreModulesPackage,


reactContext,


moduleSpecs,


reactModuleInfoMap,


jsModulesBuilder);


} finally {


Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);


}


//加载我们自定义的 ReactPackage,譬如自己封装的和 MainReactPackage 等,mPackages 就来源于我们自己定义的;整个过程同上 CoreModulesPackage,进行各种拼装 module。


// TODO(6818138): Solve use-case of native/js modules overriding


for (ReactPackage reactPackage : mPackages) {


Systrace.beginSection(


TRACE_TAG_REACT_JAVA_BRIDGE,


"createAndProcessCustomReactPackage");


try {


processPackage(


reactPackage,


reactContext,


moduleSpecs,


reactModuleInfoMap,


jsModulesBuilder);


} finally {


Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);


}


}


......


//!!!Java 层模块注册表,通过它把所有的 NativeModule 注册到 CatalystInstance。我们自定义的继承 NativeModule 接口的 Java 端也是通过他来管理。


NativeModuleRegistry nativeModuleRegistry;


try {


//new 一个 NativeModuleRegistry,其管理了 NativeModule 和 OnBatchCompleteListener 列表(JS 调用 Java 结束时的回掉管理)。


nativeModuleRegistry = new NativeModuleRegistry(moduleSpecs, reactModuleInfoMap);


} finally {


Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);


ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);


}


//依据外面是否设置 mNativeModuleCallExceptionHandler 异常捕获实现来决定 exceptionHandler 是使用外面的还是 DevSupportManager。


NativeModuleCallExceptionHandler exceptionHandler = mNativeModuleCallExceptionHandler != null


? mNativeModuleCallExceptionHandler


: mDevSupportManager;


//!!!重点创建 CatalystInstance 的 CatalystInstanceImpl 实现实例


CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()


.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())


.setJSExecutor(jsExecutor)


.setRegistry(nativeModuleRegistry)


.setJSModuleRegistry(jsModulesBuilder.build())


.setJSBundleLoader(jsBundleLoader)


.setNativeModuleCallExceptionHandler(exceptionHandler);


......


final CatalystInstance catalystInstance;


try {


catalystInstance = catalystInstanceBuilder.build();


} finally {


Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);


ReactMarker.logMarker(CREATE_CATALYST_INSTANCE_END);


}


if (mBridgeIdleDebugListener != null) {


catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);


}


//关联 reactContext 与 catalystInstance


reactContext.initializeWithInstance(catalystInstance);


//通过 catalystInstance 加载 js bundle 文件


catalystInstance.runJSBundle();


return reactContext;


}


可以发现,上面这段代码做的事情真特么多,不过总的来说 createReactContext() 方法做的都是一些取数据组表放表的过程,核心就是通过 ReactPackage 实现类的 createNativeModules()、createJSModules() 等方法把所有 NativeModule 包装后放入 NativeModuleRegistry 及 JavaScriptModule 包装后放入 JavaScriptModuleRegistry,然后把这两张映射表交给 CatalystInstanceImpl,同时包装创建 ReactContext 对象,然后通过 CatalystInstanceImpl 的 runJSBundle() 方法把 JS bundle 文件的 JS 代码加载进来等待 Task 结束以后调用 JS 入口进行渲染 RN。既然这样就去看看 CatalystInstanceImpl 的 build 方法中调用的 CatalystInstanceImpl 构造方法到底干了哪些鸟事,如下:


public class CatalystInstanceImpl implements CatalystInstance {


......


// C++ parts


private final HybridData mHybridData;


private native static HybridData initHybrid();


private CatalystInstanceImpl(


final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,


final JavaScriptExecutor jsExecutor,


final NativeModuleRegistry registry,


final JavaScriptModuleRegistry jsModuleRegistry,


final JSBundleLoader jsBundleLoader,


NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {


FLog.d(ReactConstants.TAG, "Initializing React Xplat Bridge.");


//native C++方法,用来初始化 JNI 相关状态然后返回 mHybridData。


mHybridData = initHybrid();


//创建 ReactNative 的三个线程 nativeModulesThread 和 jsThread、uiThread,都是通过 Handler 来管理的。


mReactQueueConfiguration = ReactQueueConfigurationImpl.create(


ReactQueueConfigurationSpec,


new NativeExceptionHandler());


mBridgeIdleListeners = new CopyOnWriteArrayList<>();


mJavaRegistry = registry;


mJSModuleRegistry = jsModuleRegistry;


mJSBundleLoader = jsBundleLoader;


mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;


mTraceListener = new JSProfilerTraceListener(this);


//native C++方法,用来初始化 Bridge。


initializeBridge(


new BridgeCallback(this),


jsExecutor,


mReactQueueConfiguration.getJSQueueThread(),


mReactQueueConfiguration.getNativeModulesQueueThread(),


mJavaRegistry.getModuleRegistryHolder(this));


mMainExecutorToken = getMainExecutorToken();


}


private native void initializeBridge(ReactCallback callback,


JavaScriptExecutor jsExecutor,


MessageQueueThread jsQueue,


MessageQueueThread moduleQueue,


ModuleRegistryHolder registryHolder);


......


}


刚刚分析 createReactContext() 方法的总结没错,CatalystInstanceImpl 这货就是个封装总管,负责了 Java 层代码到 JNI 封装初始化的任务和 Java 与 JS 调用的 Java 端控制中心。所以我们先看看调用 native initializeBridge 方法时传入的 5 个参数吧,分别如下:


1. callback 参数: CatalystInstanceImpl 的内部静态实现类 BridgeCallback,负责相关接口回调回传。


2. jsExecutor 参数: 前面分析的 XReactInstanceManagerImpl 中赋值为 JSCJavaScriptExecutor 实例,JSCJavaScriptExecutor 中也有自己的 native initHybrid 的 C++ 方法被初始化时调用,具体在 OnLoad.cpp 的 JSCJavaScriptExecutorHolder 类中。


3. jsQueue 参数: 来自于 mReactQueueConfiguration.getJSQueueThread(),mReactQueueConfiguration 就是 CatalystInstanceImpl 中创建的 ReactQueueConfigurationImpl.create(


ReactQueueConfigurationSpec,


new NativeExceptionHandler()); 第一个参数来自于 XReactInstanceManagerImpl 中 CatalystInstanceImpl 的建造者,实质为包装相关线程名字、类型等,然后通过 ReactQueueConfigurationImpl 的 create 创建对应线程的 Handler,这里就是名字为 js 的后台线程 Handler,第二个参数为异常捕获回调实现。


4. moduleQueue 参数: 来自于 mReactQueueConfiguration.getNativeModulesQueueThread(),mReactQueueConfiguration 就是 CatalystInstanceImpl 中创建的 ReactQueueConfigurationImpl.create(


ReactQueueConfigurationSpec,


new NativeExceptionHandler()); 第一个参数来自于 XReactInstanceManagerImpl 中 CatalystInstanceImpl 的建造者,实质为包装相关线程名字、类型等,然后通过 ReactQueueConfigurationImpl 的 create 创建对应线程的 Handler,这里就是名字为 native_modules 的后台线程 Handler,第二个参数为异常捕获回调实现。


5. registryHolder 参数: mJavaRegistry 对象来自于 XReactInstanceManagerImpl 中 CatalystInstanceImpl 的建造者,通过 mJavaRegistry.getModuleRegistryHolder(this) 传递一个 Java 层的 ModuleRegistryHolder 实例到同名的 C++ 中,具体在 mJavaRegistry.getModuleRegistryHolder(this) 的返回值处为 return new ModuleRegistryHolder(catalystInstanceImpl, javaModules, cxxModules); 而 ModuleRegistryHolder 的构造方法中调用了 C++ 的 initHybrid(catalystInstanceImpl, javaModules, cxxModules); 方法。


CatalystInstanceImpl 这货会玩,自己在 Java 层直接把持住了 JavaScriptModuleRegistry 映射表,把 NativeModuleRegistry 映射表、BridgeCallback 回调、JSCJavaScriptExecutor、js 队列 MessageQueueThread、native 队列 MessageQueueThread 都通过 JNI 嫁接到了 C++ 中。那我们现在先把目光转移到 CatalystInstanceImpl.cpp 的 initializeBridge 方法上(关于 JNI 的 OnLoad 中初始化注册模块等等就不介绍了),如下:


void CatalystInstanceImpl::initializeBridge(


jni::alias_refReactCallback::javaobject callback,


// This executor is actually a factory holder.


JavaScriptExecutorHolder* jseh,


jni::alias_refJavaMessageQueueThread::javaobject jsQueue,


jni::alias_refJavaMessageQueueThread::javaobject moduleQueue,


ModuleRegistryHolder* mrh) {


......


// Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> Bridge::Callback


// --weak--> ReactCallback -> Java CatalystInstanceImpl


......


//instance_为 ReactCommon 目录下 Instance.h 中类的实例;JNI 封装规则就不介绍了,之前写过文章的。


//第一个参数为 JInstanceCallback 实现类,父类在 cxxreact/Instance.h 中。


//第二个参数为 JavaScriptExecutorHolder,实质对应 java 中 JavaScriptExecutor,也就是上面分析 java 的 initializeBridge 方法第二个参数 JSCJavaScriptExecutor。


//第三第四个参数都是 java 线程透传到 C++,纯 C++的 JMessageQueueThread。


//第五个参数为 C++的 ModuleRegistryHolder 的 getModuleRegistry()方法。


instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),


jseh->getExecutorFactory(),


folly::make_unique<JMessageQueueThread>(jsQueue),


folly::make_unique<JMessageQueueThread>(moduleQueue),


mrh->getModuleRegistry());


}


到此 CatalystInstance 的实例 CatalystInstanceImpl 对象也就初始化 OK 了,同时通过 initializeBridge 建立了 Bridge 连接。关于这个 Bridge 在 RN 中是通过 libjsc.so 中 JSObjectRef.h 的 JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL); 来关联的,这样就可以在 Native 设置 JS 执行,反之同理。


由于这一小节我们只讨论 RN 的加载启动流程,所以 initializeBridge 的具体实现我们下面分析互相通信交互时再仔细分析,故我们先把思路还是回到 XReactInstanceManagerImpl 中 createReactContext 方法的 reactContext.initializeWithInstance(catalystInstance); 一行,可以看见,这行代码意思就是将刚刚初始化的 catalystInstance 传递给全局唯一的 reactContext 对象,同时在 reactContext 中通过 catalystInstance 拿到 JS、Native、UI 几个 Thread 的引用,方便快速访问使用这些对象。接着调用了 catalystInstance.runJSBundle(); 方法,这个方法实现如下:


@Override


public void runJSBundle() {


......


mJSBundleHasLoaded = true;


//mJSBundleLoader 就是前面分析的依据不同设置决定是 JSBundleLoader 的 createAssetLoader 还是 createFileLoader 等静态方法的匿名实现类。


// incrementPendingJSCalls();


mJSBundleLoader.loadScript(CatalystInstanceImpl.this);


......


}


通过注释我们假设 Loader 是默认的,也即 JSBundleLoader 类的如下方法:


public static JSBundleLoader createAssetLoader(


final Context context,


final String assetUrl) {


return new JSBundleLoader() {


@Override


public void loadScript(CatalystInstanceImpl instance) {


instance.loadScriptFromAssets(context.getAssets(), assetUrl);


}


@Override


public String getSourceUrl() {


return assetUrl;


}


};


}


可以看见,它实质又调用了 CatalystInstanceImpl 的 loadScriptFromAssets 方法,我们继续跟踪 CatalystInstanceImpl 的这个方法吧,如下:


native void loadScriptFromAssets(AssetManager assetManager, String assetURL);


loadScriptFromAssets 既然是一个 native 方法,我们去 CatalystInstanceImpl.cpp 看下这个方法的实现,如下:


void CatalystInstanceImpl::loadScriptFromAssets(jobject assetManager,


const std::string& assetURL) {


const int kAssetsLength = 9; // strlen("assets://");


//获取 source 路径名,不计前缀,这里默认就是 index.android.bundle


auto sourceURL = assetURL.substr(kAssetsLength);


//assetManager 是 Java 传递的 AssetManager。


//extractAssetManager 是 JSLoader.cpp 中通过系统动态链接库 android/asset_manager_jni.h 的 AAssetManager_fromJava 方法来获取 AAssetManager 对象的。


auto manager = react::extractAssetManager(assetManager);


//通过 JSLoader 对象的 loadScriptFromAssets 方法读文件,得到大字符串 script(即 index.android.bundle 文件的 JS 内容)。


auto script = react::loadScriptFromAssets(manager, sourceURL);


//判断是不是 Unbundle,这里不是 Unbundle,因为打包命令我们用了 react.gradle 的默认 bundle,没用 unbundle 命令(感兴趣的自己分析这条路线)。


if (JniJSModulesUnbundle::isUnbundle(manager, sourceURL)) {


instance_->loadUnbundle(


folly::make_unique<JniJSModulesUnbundle>(manager, sourceURL),


std::move(script),


sourceURL);


return;


} else {


//bundle 命令打包的,所以走这里。


//instance_为 ReactCommon 目录下 Instance.h 中类的实例,前面分析过了。


instance_->loadScriptFromString(std::move(script), sourceURL);


}


}


看来还没到头,这货又走到了 ReactCommon 目录下 Instance 实例的 loadScriptFromString 方法去了(由此可以看出来前面 ReactNativeAndroid 目录下的 jni 代码都是 Android 平台特有的封装,直到 ReactCommon 才是通用的),如下:


//string 为 index.android.bundle 内容。


//sourceURL 在这里默认为 index.android.bundle。


void Instance::loadScriptFromString(std::unique_ptr<const JSBigString> string,


std::string sourceURL) {


//callback_就是 initializeBridge 传进来的,实质实现是 CatalystInstanceImpl 的 BridgeCallback。


//说白了就是回传一个状态,要开始搞 loadScriptFromString 了


callback_->incrementPendingJSCalls();


SystraceSection s("reactbridge_xplat_loadScriptFromString",


"sourceURL", sourceURL);


//厉害了,Word 哥,年度大戏啊!


//nativeToJsBridge_也是 Instance::initializeBridge 方法里初始化的,实现在 Common 的 NativeToJsBridge 类里。


nativeToJsBridge_->loadApplication(nullptr, std::move(string), std::move(sourceURL));


}


妈的,没完没了了,继续跟吧,到 Common 的 NativeToJsBridge.cpp 看看 loadApplication 方法吧,如下:


//unbundle 传入的是个空指针。


//startupScript 为 bundle 文件内容。


//startupScript 为 bundle 文件名。


void NativeToJsBridge::loadApplication(


std::unique_ptr<JSModulesUnbundle> unbundle,


std::unique_ptr<const JSBigString> startupScript,


std::string startupScriptSourceURL) {


//runOnExecutorQueue 实质就是获取一个 MessageQueueThread,然后在其线程中执行一个 task。


runOnExecutorQueue(


m_mainExecutorToken,


[unbundleWrap=folly::makeMoveWrapper(std::move(unbundle)),


startupScript=folly::makeMoveWrapper(std::move(startupScript)),


startupScriptSourceURL=std::move(startupScriptSourceURL)]


(JSExecutor* executor) mutable {


auto unbundle = unbundleWrap.move();


if (unbundle) {


executor->setJSModulesUnbundle(std::move(unbundle));


}


//因为我们是 bundle 命令打包的,所以走这里继续执行!!!


executor->loadApplicationScript(std::move(*startupScript),


std::move(startupScriptSourceURL));


});


}


靠靠靠,还不到头,又特么绕到 JSExecutor 的 loadApplicationScript 方法里面去了,继续跟吧(这个 executor 是 runOnExecutorQueue 方法中回传的一个 map 中取的,实质是 OnLoad 中 JSCJavaScriptExecutorHolder 对应,也即 java 中 JSCJavaScriptExecutor,所以 JSExecutor 实例为 JSCExecutor.cpp 中实现),如下:


//script 为 bundle 文件内容,sourceURL 为 bundle 文件名


void JSCExecutor::loadApplicationScript(std::unique_ptr<const JSBigString> script, std::string sourceURL) throw(JSException) {


SystraceSection s("JSCExecutor::loadApplicationScript",


"sourceURL", sourceURL);


......


//把 bundle 文件和文件名等内容转换成 js 可以识别的 String


String jsScript = jsStringFromBigString(*script);


String jsSourceURL(sourceURL.c_str());


//使用 webkit JSC 去真正解释执行 Javascript 了!


evaluateScript(m_context, jsScript, jsSourceURL);


//绑定桥,核心是通过 getGlobalObject 将 JS 与 C++通过 webkit JSC bind


bindBridge();


flush();


......


}


去他大爷的,没完没了了,继续看看 bindBridge() 方法和 flush() 方法,如下:


void JSCExecutor::bindBridge() throw(JSException) {


......


auto global = Object::getGlobalObject(m_context);


auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");


......


auto batchedBridge = batchedBridgeValue.asObject();


m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();


m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();


//通过 webkit JSC 获取 MessageQueue.js 的 flushedQueue


m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();


m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();


}


void JSCExecutor::flush() {


SystraceSection s("JSCExecutor::flush");


//m_flushedQueueJS->callAsFunction({})即调用 MessageQueue.js 的 flushedQueue 方法。


//即把 JS 端相关通信交互数据通过 flushedQueue 返回传给 callNativeModules。


callNativeModules(m_flushedQueueJS->callAsFunction({}));


}


void JSCExecutor::callNativeModules(Value&& value) {


SystraceSection s("JSCExecutor::callNativeModules");


try {


//把 JS 端相关通信数据转为 JSON 格式字符串数据


auto calls = value.toJSONString();


//m_delegate 实质为 Executor.h 中 ExecutorDelegate 类的实现类 JsToNativeBridge 对象。


//故 callNativeModules 为 JsToNativeBridge.cpp 中实现的方法,把 calls json 字符串 pase 成格式结构。


m_delegate->callNativeModules(*this, folly::parseJson(calls), true);


} catch (...) {


......


}


}


卧槽!又绕回到了 JsToNativeBridge.cpp 的 callNativeModules 方法,那就看下吧,如下:


//executor 即为前面的 JSCExecutor。


//calls 为被解析 OK 的 JS 端 JSON 通信参数结构。


//isEndOfBatch 通知是否一个批次处理 OK 了,这里传递了 true 进来,说明 JS 文件 Loader OK 了。


void callNativeModules(


JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {


//拿到 token


ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);


//扔到 nativeQueue 的线程队列去等待执行


m_nativeQueue->runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable {


// An exception anywhere in here stops processing of the batch. This


// was the behavior of the Android bridge, and since exception handling


// terminates the whole bridge, there's not much point in continuing.


for (auto& call : react::parseMethodCalls(std::move(calls))) {


//调用 Native registry 表中的 java NativeMethod 方法。


m_registry->callNativeMethod(


token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);


}


//一些类似数据库事务操作的机制,用来告诉 OK 了


if (isEndOfBatch) {


m_callback->onBatchComplete();


m_callback->decrementPendingJSCalls();


}


});


}


终于尼玛明朗了,上面这段调用不就是前面分析的那个回调么,说白了就是 CatalystInstanceImpl.java 中 CatalystInstanceImpl 构造方法中调用 C++ 的 initializeBridge 方法时传入的第一个参数 BridgeCallback 么,也就是说 JS bundle 文件被加载完成以后 JS 端调用 Java 端时会触发 Callback 的 onBatchComplete 方法,这货最终又会触发 OnBatchCompleteListener 接口的 onBatchComplete 方法,这不就把 JS Bundle 文件加载完成以后回调 Java 通知 OK 了么,原来主要的流程是这么回事。为了接下来不迷糊,赶紧先来一把小梳理总结,用图说话,如下:



上面这幅图已经囊括了我们上面那些枯燥的启动流程的部分流程分析了,好了,从上图可以知道我们前面贴出来的 AsyncTask 的 onPostExecute 方法还没分析,所以我们把目光再回到 XReactInstanceManagerImpl 的那个 ReactContextInitAsyncTask 中,doInBackground 方法执行完成后返回了 Result 包装的 reactContext,所以我们看下 onPostExecute 方法中调用的核心方法 setupReactContext,如下:


private void setupReactContext(ReactApplicationContext reactContext) {


......


CatalystInstance catalystInstance =


Assertions.assertNotNull(reactContext.getCatalystInstance());


//执行 Native Java Module 的 initialize


catalystInstance.initialize();


//重置 DevSupportManager 实现类的 reactContext 相关


mDevSupportManager.onNewReactContextCreated(reactContext);


//内存状态回调设置


mMemoryPressureRouter.addMemoryPressureListener(catalystInstance);


//置位生命周期


moveReactContextToCurrentLifecycleState();


//核心方法!!!!


for (ReactRootView rootView : mAttachedRootViews) {


attachMeasuredRootViewToInstance(rootView, catalystInstance);


}


......


}


到此我们再追一下 mAttachedRootViews 这个列表赋值的地方吧,依旧是这个类的 attachMeasuredRootView(ReactRootView rootView) 方法,然而这个方法唯一被调用的地方在 ReactRootView 的 attachToReactInstanceManager() 中,再次发现 attachToReactInstanceManager 又是在 ReactRootView 已经 measure 的情况下才会触发,所以也就是说 mAttachedRootViews 中保存的都是 ReactRootView。那我们继续回到 XReactInstanceManagerImpl 中 setupReactContext 方法的 attachMeasuredRootViewToInstance(rootView, catalystInstance)


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


; 里看看,如下:


private void attachMeasuredRootViewToInstance(


ReactRootView rootView,


CatalystInstance catalystInstance) {


......


//彻底 reset ReactRootView 中的 UI


// Reset view content as it's going to be populated by the application content from JS


rootView.removeAllViews();


rootView.setId(View.NO_ID);


//通过 UIManagerModule 设置根布局为 ReactRootView


UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class);


int rootTag = uiManagerModule.addMeasuredRootView(rootView);


//设置相关 tag


rootView.setRootViewTag(rootTag);


//把 Java 端启动传递的 launchOptions 包装成 JS 用的类型


@Nullable Bundle launchOptions = rootView.getLaunchOptions();


WritableMap initialProps = Arguments.makeNativeMap(launchOptions);


//获取我们 startReactApplication 设置的 JS 端入口 name,继承 ReactActivity 的话值为 getMainComponentName()设置的


String jsAppModuleName = rootView.getJSModuleName();


//包装相关参数,rootTag 告知 JS 端 Native 端的 ReactRootView 是哪个


WritableNativeMap appParams = new WritableNativeMap();


appParams.putDouble("rootTag", rootTag);


appParams.putMap("initialProps", initialProps);


//核心大招!!!!!React Native 真正的启动流程入口是被 Java 端在这里拉起来的!!!!!!!!


catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);


......


}


坚持一下,分析源码就是个苦逼的过程,坚持下来就好了,马上看到希望了;我们知道 AppRegistry.class 是 JS 端暴露给 Java 端的接口方法,所以 catalystInstance.getJSModule(AppRegistry.class) 实质就桥接到 JS 端代码去了,那就去看看 AppRegistry.js 的代码吧,如下:


//JS 端对应代码,注意这个变量上面的英文已经交代很详细啦


var AppRegistry = {


......


//我们 JS 端自己在 index.android.js 文件中调用的入口就是:


//AppRegistry.registerComponent('TestRN', () => TestRN);


registerComponent: function(appKey: string, getComponentFunc: ComponentProvider): string {


runnables[appKey] = {


run: (appParameters) =>


renderApplication(getComponentFunc(), appParameters.initialProps, appParameters.rootTag)


};


return appKey;


},


......


//上面 java 端 AppRegistry 调用的 JS 端就是这个方法,索引到我们设置的 appkey=TestRN 字符串的 JS 入口


runApplication: function(appKey: string, appParameters: any): void {


......


runnables[appKey].run(appParameters);


},


......


};


真他妈不容易啊,总算到头了,原来 React Native 是这么被启动起来的。现在回过头来看发现其实主启动流程也就那么回事,还以为很神秘嘻嘻的,现在总算被揭开了。总结一下吧,如下图所示即为整个 React Native 加载主流成的主要情况:



到这里 React Native 的启动流程就分析完了,不过,我猜你看到这里的时候一定会骂我,因为我知道上面的主流程中你会有很多疑惑,这也是我写这篇阅读 RN 源码总结最纠结的地方,因为想尽可能的将主加载流程和通信方式分开来分析,以便做到模块化理解,但是后来发现关联性又很强,揉一起分析更乱套,所以就有了这么一篇很长的文章,前面就当是主流程综述概要分析,细节在下面通信方式分析时会继续提及浅析,所以建议带着上面的疑惑继续向下看完这篇文章再回到 Part 2 RN 启动流程框架浅析 这一部分来看一遍,这样你的疑惑就全部揭开了。


**【工匠若水 [http://blog.csdn.net/yanbober](


) 未经允许严禁转载,请尊重作者劳动成果。[私信联系我](


)】**


3 RN Java 调用 JS 端框架浅析


=========================


这是一个悲伤的故事,看源码没有伴,RN 接入也在自己一个人搞,所以搞起来总是挺慢,好在一直在坚持,源码也断断续续在工作之余看了一个多星期,这篇文章也占用了我一个美好的周末时光,有种说不出来的感觉,唉,不扯了,我们现在来看看 RN 中 Java 是如何调用 JS 代码的。


首先,通过上面加载流程或者以前我们自定义 Java & JS 交互模块的经历我们知道 JS 端代码模块对应的 Java 端都是继承 JavaScriptModule 来实现的(可以看上面 reactPackage.createJSModules() 方法,返回的是 JS 端给 Java 端约定好的 JS 模块 Java 实现);要说 Java 端如何调用 JS 端代码就得有个例子,我们就拿上面启动流程中最后 CatalystInstanceImpl.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams); 拉起 JS 端 index.android.js 的 JS 组件入口来分析,这就是一个典型的 Java 端调用 JS 端代码的例子,首先我们可以知道 AppRegistry.java 是继承 JavaScriptModule 的,如下:


public interface AppRegistry extends JavaScriptModule {


void runApplication(String appKey, WritableMap appParameters);


void unmountApplicationComponentAtRootTag(int rootNodeTag);


void startHeadlessTask(int taskId, String taskKey, WritableMap data);


}


然后 AppRegistry.java 是在 CoreModulesPackage 的 createJSModules() 方法中被添加入列表的,CoreModulesPackage 又是在主启动流程的 processPackage() 方法中被包装后加入 JavaScriptModuleRegistry 映射表的,JavaScriptModuleRegistry 映射表又被 Java 层的 CatalystInstanceImpl 接管。所以 Java 调用 JS 方法都是通过 CatalystInstanceImpl.getJSModule(class).methodXXX() 来执行的(我们自己模块调用的话是通过 ReactContext.getJSModule(),因为 ReactContext 在主启动流程中持有了 CatalystInstanceImpl 实例,所以 CatalystInstanceImpl 是不直接对外的),那我们就沿着这条线去观摩一把,如下 CatalystInstanceImpl.java 的 getJSModule 方法:


@Override


public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {


//mMainExecutorToken 来自于 native C++代码


return getJSModule(mMainExecutorToken, jsInterface);


}


@Override


public <T extends JavaScriptModule> T getJSModule(ExecutorToken executorToken, Class<T> jsInterface) {


//mJSModuleRegistry 就是启动流程中 processPackage()方法加进去交给 CatalystInstanceImpl 托管的 JS 代码映射表


return Assertions.assertNotNull(mJSModuleRegistry)


.getJavaScriptModule(this, executorToken, jsInterface);


}


接着去 JavaScriptModuleRegistry 映射表中看看 getJavaScriptModule() 方法,如下:


public synchronized <T extends JavaScriptModule> T getJavaScriptModule(


CatalystInstance instance,


ExecutorToken executorToken,


Class<T> moduleInterface) {


//module 加载的缓存,加载过一次且缓存存在就直接从缓存取


......


//获取 JavaScriptModule 模块的方式,以 AppRegistry 模块获取为例,略叼,动态代理生成获取 JS Module


JavaScriptModuleRegistration registration =


Assertions.assertNotNull(


mModuleRegistrations.get(moduleInterface),


"JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");


JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(


moduleInterface.getClassLoader(),


new Class[]{moduleInterface},


new JavaScriptModuleInvocationHandler(executorToken, instance, registration));


instancesForContext.put(moduleInterface, interfaceProxy);


return (T) interfaceProxy;


}

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
React Native Android 源码框架浅析(主流程及 Java 与 JS 双边通信)