之前更新过iOS版本的初始化流程以及OC和JS相互调用的原理,今天跟大家更新一下Android版本的流程以及原理分析。投稿人:Desmond Yao地址:http://blog.desmondyao.com/
本文所讲知识点
JSBridge初始化过程
JS加载Java模块
Java执行JS
简单整理了一下几个组件之间的关系,放在前面:
今天主要解析RN4A里面的Native&JS通信机制。
注:Java在RN4A中是Native模块,涉及JNI的部分在java中术语为native,注意大小写的区分不要混淆。
在ReactInstanceManager初始化时会创建ReactContext,其中主要的一部分工作就是注册Native&js模块,我们看看它都做了什么:
1.1.模块注册
在createReactContext()中会先注册CoreModulesPackage的Native&JS模块与所有的ViewManager,之后注册在ReactNativeHost中(0.28之前的版本在ReactActivity中)声明的所有其他ReactPackage的Native&JS模块,其中:
Native模块注册
将所有的NativeModule添加进NativeModuleRegistry.Builder后,它会依次build每一个NativeModule,主要做的事情就是生成moduleID、解析带@ReactMethod注解的方法;
JavaScript模块注册
①.在js层,js模块在写的时候都需要加上BatchedBridge.registerCallableModule('module', Module);注册到BatchedBridge.js中以供后续查找;
②.在java层,js模块将想要暴露出来的方法声明为一个接口类,它被build的时候利用Java动态代理生成实例, 具体的方法invoke由CatalystInstance.callFunction代理执行。
注册完后,会初始化CatalystInstance, 模块注册、build生成后的NativeModuleRegistry与JavaScriptModuleRegistry都由CatalystInstance持有.
1.2 初始化ReactBridge
CatalystInstance初始化时会初始化ReactBridge,ReactBridge是在Java层与js沟通的桥梁(废话..),它是一个native类,大部分实现位于Bridge.h/.cpp,在初始化时调用的native initialize()方法对应OnLoad.cpp中的create函数,它的需要三个参数:
JavaScriptExecutor
RN4A使用WebKit的JavaScriptCore(JSCore)来解析js, JavaScriptExecutor的实现类JSCJavaScriptExecutor是一个native类,它封装了JSCore的逻辑(对应JSCExecutor.h/.cpp)。
ReactCallback
为JavaScript提供Java module执行入口。
MessageQueueThread
包装着Java模块执行的线程Handler。 注: RN4A里面有三个主要执行线程:UI线程, JS线程, Java线程.
我们看一下它的初始化过程:
实际上做的工作也不多,就是一堆封装,最后生成了一个继承Countable的Bridge实例,继承Countable的对象的内存是由native分配的,也由native回收。
CatalystInstance初始化完ReactBridge后, 创建NativeModuleRegistry的JSON至并放在javascript的全局变量global.__fbBatchedBridgeConfig中:
查看图片
在buildModulesConfigJSONProperty函数中我们可以看出Native模块打包出的json数据结构大概为:查看图片
这里的bridge.setGlobalVariable最后到JSCExecutor.setGlobalVariable中,通过JavaScriptCore::JSObjectSetProperty实现。
JS加载Java模块在Java模块被封装好注入到gloabl.__fbBatchedBridgeConfig后,js需要进行一些处理,这里有三个模块需要注意:NativeModule.js, BatchedBridge.js, MessageQueue.js,我们来看看RN4A是怎么一步步载入Native模块的:
js模块中我们都会require('react-native')这样(或者ES6的import xxx from ('react-native')),此时加载的react-native.js模块中,会加载NativeModule.js模块,NativeModule.js又会去加载BatchedBridge.js,在这里解析之前传入的global.__fbBatchedBridgeConfig;
BatchedBridge.js里面封装了MessageQueue的一个实例,它将global.__fbBatchedBridgeConfig传给了MessageQueue的构造函数,我们看看构造函数里对它干了什么:
这段代码还算浅显易懂,它定义了一个RemoteModules属性,其中将模块、方法、常量什么的解析为js可用的变量、函数。lazyProperty函数实际上就是Object.defineProperty函数里面对property指定get(),不直接初始化。
再加上之前所说的,需要暴露给java的JS模块都会调用BatchedBridge.registerCallableModule来注册自己,最后也会跑到MessageQueue里面,这样一来,所有的Native&JS模块入口都集中在了MessageQueue里面。BatchedBridge最后将自己定义为了一个全局变量,方便JSCore直接找到它:查看图片
1.加载好BatchedBridge.js后,回到NativeModule.js, 它BatchedBridge.RemoteModules又做了一点处理:查看图片
于是我们就会看到RN4A官方文档上所说的,如果你定义了一个Native模块,需要额外加一个文件,声明如下信息:查看图片
这下就明白了,因为所有Native的模块都会在加载的时候注册到NativeModules里面。
Java执行JS上面说到所有的JavaScript模块的方法都通过动态代理交给CatalystInstance.callFunction来执行,那么具体是怎么被执行的呢,我们来看看查看图片
在JSCExecutor.cpp这一层,它会生成JS调用gloabl.__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null, [module, method, args])交由JavascriptCore来执行。结合上文分析,这个gloabl.__fbBatchedBridge就是MessageQueue。
MessageQueue.callFunctionReturnFlushedQueue里面直接调用了__callFunction()函数,我们看看它是怎么找JS模块执行的:
this._callableModules里面就存放了所有由BatchedBridge.registerCallableModule注册上来的java模块,然后再找对应方法执行即可。
最后说两句关于JS如何执行Java的,下回继续更新分析,更多详情请大家点击底部阅读原文查看。
本公众号欢迎大家投稿,如果你希望你的文章可以被更多人看到,直接将md、doc等格式的文章到我邮箱即可(781931404@qq.com),也可以加我QQ/微信(781931404)好友,需要注明(投稿),谢谢。