转载

【React Native for Android】jsBridge实现原理

基于源码版本:0.28

简单整理了一下几个组件之间的关系,放在前面:

【React Native for Android】jsBridge实现原理

1.JSBridge初始化过程

React Native for Android(RN4A)的核心流程在QZone的 框架启动核心路径剖析 一文中讲述得很详细,本文不再赘述,主要解析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模块注册
    1. 在js层,js模块在写的时候都需要加上 BatchedBridge.registerCallableModule('module', Module); 注册到 BatchedBridge.js 中以供后续查找;
    2. 在java层,js模块将想要暴露出来的方法声明为一个接口类,它被build的时候利用Java动态代理生成实例, 具体的方法invoke由 CatalystInstance.callFunction 代理执行。

注册完后,会初始化 CatalystInstance , 模块注册、build生成后的 NativeModuleRegistryJavaScriptModuleRegistry 都由 CatalystInstance 持有.

1.2 初始化ReactBridge

CatalystInstance 初始化时会初始化 ReactBridgeReactBridge 是在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线程 .

我们看一下它的初始化过程:

staticvoidcreate(JNIEnv* env, jobject obj, jobject executor, jobject callback,  jobject callbackQueueThread) { autoweakCallback = createNew<WeakReference>(callback); autoweakCallbackQueueThread = createNew<WeakReference>(callbackQueueThread); autobridgeCallback = folly::make_unique<PlatformBridgeCallback>(weakCallback, weakCallbackQueueThread); autonativeExecutorFactory = extractRefPtr<CountableJSExecutorFactory>(env, executor); autoexecutorTokenFactory = folly::make_unique<JExecutorTokenFactory>(); autobridge = createNew<CountableBridge>(nativeExecutorFactory.get(),std::move(executorTokenFactory),std::move(bridgeCallback));  setCountableForJava(env, obj, std::move(bridge)); } 

实际上做的工作也不多,就是一堆封装,最后生成了一个继承 CountableBridge 实例,继承 Countable 的对象的内存是由native分配的,也由native回收。

CatalystInstance 初始化完 ReactBridge 后, 创建 NativeModuleRegistry 的JSON至并放在javascript的全局变量 global.__fbBatchedBridgeConfig 中:

bridge.setGlobalVariable( "__fbBatchedBridgeConfig",  buildModulesConfigJSONProperty(mJavaRegistry)); 

buildModulesConfigJSONProperty 函数中我们可以看出Native模块打包出的json数据结构大概为:

remoteModuleConfig: {  moduleName : {  moduleId: 0,  supportsWebWorkers: false,  methods: {  methodName : {  methodId: 0,  methodType: remote  } // other methods ...  },  constants: {  constant1: 123,  constant2: 234  }  }, // other modules ... } 

这里的 bridge.setGlobalVariable 最后到 JSCExecutor.setGlobalVariable 中,通过 JavaScriptCore::JSObjectSetProperty 实现。

JS加载Java模块

在Java模块被封装好注入到 gloabl.__fbBatchedBridgeConfig 后,js需要进行一些处理,这里有三个模块需要注意: NativeModule.js , BatchedBridge.js , MessageQueue.js ,我们来看看RN4A是怎么一步步载入Native模块的:

  1. js模块中我们都会 require('react-native') 这样(或者ES6的 import xxx from ('react-native') ),此时加载的 react-native.js 模块中,会加载 NativeModule.js 模块, NativeModule.js 又会去加载 BatchedBridge.js ,在这里解析之前传入的 global.__fbBatchedBridgeConfig ;

  2. BatchedBridge.js 里面封装了 MessageQueue 的一个实例,它将 global.__fbBatchedBridgeConfig 传给了 MessageQueue 的构造函数,我们看看构造函数里对它干了什么:

lazyProperty(this,'RemoteModules', () => { const{remoteModuleConfig} = configProvider(); constmodulesConfig =this._genModulesConfig(remoteModuleConfig); constmodules =this._genModules(modulesConfig);  //生成调试用的Native module/method查找表  returnmodules; }); 

这段代码还算浅显易懂,它定义了一个 RemoteModules 属性,其中将模块、方法、常量什么的解析为js可用的变量、函数。 lazyProperty 函数实际上就是 Object.defineProperty 函数里面对property指定 get() ,不直接初始化。

再加上之前所说的,需要暴露给java的JS模块都会调用 BatchedBridge.registerCallableModule 来注册自己,最后也会跑到 MessageQueue 里面,这样一来,所有的Native&JS模块入口都集中在了 MessageQueue 里面。 BatchedBridge 最后将自己定义为了一个全局变量,方便JSCore直接找到它:

Object.defineProperty(global,'__fbBatchedBridge', { value: BatchedBridge }); 
  1. 加载好 BatchedBridge.js 后,回到 NativeModule.js , 它 BatchedBridge.RemoteModules 又做了一点处理:
constNativeModules = {}; Object.keys(RemoteModules).forEach((moduleName) => { Object.defineProperty(NativeModules, moduleName, {  configurable: true,  enumerable: true,  get: () => { letmodule= RemoteModules[moduleName];  //IOS用,如果注入了nativeRequireModuleConfig方法,则由它生成moduleConfig if(module&&typeofmodule.moduleID ==='number'&& global.nativeRequireModuleConfig) { constjson = global.nativeRequireModuleConfig(moduleName); constconfig = json &&JSON.parse(json); module= config && BatchedBridge.processModuleConfig(config,module.moduleID);  RemoteModules[moduleName] = module;  }  //将每个生成的module结构定义为自己的变量 Object.defineProperty(NativeModules, moduleName, {  configurable: true,  enumerable: true,  value: module,  }); returnmodule;  },  }); }); 

于是我们就会看到RN4A官方文档上所说的,如果你定义了一个Native模块,需要额外加一个文件,声明如下信息:

import{ NativeModules }from'react-native'; module.exports = NativeModules.YourNativeModule; 

这下就明白了,因为所有Native的模块都会在加载的时候注册到 NativeModules 里面。

Java执行JS

上面说到所有的JavaScript模块的方法都通过动态代理交给 CatalystInstance.callFunction 来执行,那么具体是怎么被执行的呢,我们来看看

【React Native for Android】jsBridge实现原理

JSCExecutor.cpp 这一层,它会生成JS调用 gloabl.__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null, [module, method, args]) 交由JavascriptCore来执行。结合上文分析,这个 gloabl.__fbBatchedBridge 就是 MessageQueue

MessageQueue.callFunctionReturnFlushedQueue 里面直接调用了 __callFunction() 函数,我们看看它是怎么找JS模块执行的:

__callFunction(module: string, method: string, args: any) { //标记时间, 开始systrace constmoduleMethods =this._callableModules[module]; //检查module合法性  moduleMethods[method].apply(moduleMethods, args); //结束systrace } 

this._callableModules 里面就存放了所有由 BatchedBridge.registerCallableModule 注册上来的java模块,然后再找对应方法执行即可。

关于js如何执行java的,请看下文分解吧

原文  http://blog.desmondyao.com/2016/07/02/rn-bridge/
正文到此结束
Loading...