本文通过分析源码,逐步解析 ReactNative 中 Native to JS 的通信机制。
本文同时发表于我的个人博客
Native to JS
在『ReactNative源码解析——通信机制详解(1/2)』一文中通过 RN 源码逐步分析了 JS to Native 的通信机制,整个过程相当还是比较复杂。本文同样通过解析源码,逐步分析 Native to JS 的过程。 相比 JS to Native,Native to JS 简单不少。
这是我们在『ReactNative源码解析——通信机制详解(1/2)』一文中给出的 RN 关键类类图。 上图是 Native to JS 的时序图,RCTBridge
作为 RN 对外接口,Native 调用 JS 的方法自然也需要从此发出。上图中的方法调用链比较简单,不一一分析。
在『ReactNative源码解析——通信机制详解(1/2)』中,我们知道NativeToJsBridge
是 Native to JS 的桥接,NativeToJsBridge
做的最重要的一件事就是线程管理——使所有的 JS 调用都在指定的线程上执行。(RN中关于线程问题,后面会另开文章专门讨论。)
JSCExecutor
是 RN 中 JS 执行引擎,今天的分析就从此开始。
JS to Native 的流程进入JSCExecutor::callFunction
。
JSCExecutor::callFunction
void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
auto result = [&] {
if (!m_callFunctionReturnResultAndFlushedQueueJS) {
bindBridge();
}
return m_callFunctionReturnFlushedQueueJS->callAsFunction({
Value(m_context, String::createExpectingAscii(m_context, moduleId)),
Value(m_context, String::createExpectingAscii(m_context, methodId)),
Value::fromDynamic(m_context, std::move(arguments))
});
}();
callNativeModules(std::move(result));
}
在callFunction
方法中,首先判断环境是否准备好(第3
行),若尚未准备好,则进入bindBridge
void JSCExecutor::bindBridge() throw(JSException) {
std::call_once(m_bindFlag, [this] {
auto global = Object::getGlobalObject(m_context);
auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
auto batchedBridge = batchedBridgeValue.asObject();
m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
});
}
bindBridge
方法主要完成一些初始化准备工作:从 JS 则获取batchedBridge
object 以及若干个方法(m_callFunctionReturnFlushedQueueJS
等)。
在BatchedBridge.js
中可以找到__fbBatchedBridge
的定义:
const BatchedBridge = new MessageQueue();
Object.defineProperty(global, '__fbBatchedBridge', {
configurable: true,
value: BatchedBridge,
});
可以看到,在 Native 里面拿到的batchedBridge
是一个MessageQueue
类型的 JS object。
m_callFunctionReturnFlushedQueueJS
则是 JS 类MessageQueue
的callFunctionReturnFlushedQueue
方法。
回到JSCExecutor::callFunction
,在第6
行执行了callFunctionReturnFlushedQueue
方法。
MessageQueue.callFunctionReturnFlushedQueue(JS)
callFunctionReturnFlushedQueue(module: string, method: string, args: Array<any>) {
this.__guard(() => {
this.__callFunction(module, method, args);
});
return this.flushedQueue();
}
callFunctionReturnFlushedQueue
方法调用了内部的__callFunction
方法。
__callFunction(module: string, method: string, args: Array<any>) {
this._lastFlush = new Date().getTime();
this._eventLoopStartTime = this._lastFlush;
const moduleMethods = this._getCallableModule(module);
const result = moduleMethods[method].apply(moduleMethods, args);
return result;
}
__callFunction
通过 moduleName
在 JS Module 注册表中找到该 module,并调用相应的方法。
PS:
JSCExecutor::callFunction
->MessageQueue.callFunctionReturnFlushedQueue
这套接口不会将要调用的 JS 方法的返回值传给 Native 侧。 如需返回 JS 方法的返回值,可调用另外一套接口:JSCExecutor::callFunctionSyncWithValue
->MessageQueue.callFunctionReturnResultAndFlushedQueue
。 but,在callFunctionSyncWithValue
方法的声明处有注释:***『This method is experimental, and may be modified or removed』***
JS Module 注册表
上一节,我们提到 JS Modulde 注册表(_lazyCallableModules
),所有曝露给 Native 的 JS Module都需要注册。
registerCallableModule(name: string, module: Object) {
this._lazyCallableModules[name] = () => module;
}
registerLazyCallableModule(name: string, factory: void => Object) {
let module: Object;
let getValue: ?(void => Object) = factory;
this._lazyCallableModules[name] = () => {
if (getValue) {
module = getValue();
getValue = null;
}
return module;
};
}
_getCallableModule(name: string) {
return this._lazyCallableModules[name]();
}
JS Module 注册表支持懒加载。
可通过registerCallableModule
或registerLazyCallableModule
接口注册。
如在RCTEventEmitter.js中注册的消息模块RCTEventEmitter
:
BatchedBridge.registerCallableModule('RCTEventEmitter', eventEmitter);
在InitializeCore.js中注册的基础模块(懒加载):
BatchedBridge.registerLazyCallableModule('Systrace', () => require('Systrace'));
BatchedBridge.registerLazyCallableModule('JSTimers', () => require('JSTimers'));
BatchedBridge.registerLazyCallableModule('HeapCapture', () => require('HeapCapture'));
BatchedBridge.registerLazyCallableModule('SamplingProfiler', () => require('SamplingProfiler'));
BatchedBridge.registerLazyCallableModule('RCTLog', () => require('RCTLog'));
BatchedBridge.registerLazyCallableModule('RCTDeviceEventEmitter', () => require('RCTDeviceEventEmitter'));
BatchedBridge.registerLazyCallableModule('RCTNativeAppEventEmitter', () => require('RCTNativeAppEventEmitter'));
BatchedBridge.registerLazyCallableModule('PerformanceLogger', () => require('PerformanceLogger'));
至此,Native to JS 的流程基本结束。
but,事情并没有结束。
前面讲到,callFunctionReturnFlushedQueue
不会返回所调 JS 方法的返回值,但它确有返回值(从 JS 传给 Native):
// callFunctionReturnFlushedQueue的 return 语句
return this.flushedQueue();
在分析 JS to Native 时介绍过,出于性能考虑所有从 JS to Native 的调用都会先入队,只有满足一定的条件(离上一次 flush queue 大于5ms)才会被执行。 所有 Native to JS的调用,在其结束时都会触发一次 flush queue 的操作,即 flush 所有入队的 JS to Native 的调用。
小结
Native to JS 的通信过程相对比较简单,总结主要有两点:
- 所有曝露给 Native 的 JS Module 都需要提前注册;
- 在 Native to JS 调用结束时,会触发 flush JS to Native Queue 的操作。
总结
RN 中 Native 与 JS 的通信机制基本分析完成,总结主要有以下几点:
- RN 项目中涉及多种语言,但 Native 与 JS 的通信发生在
C++
与JavaScript
间; - 双方具体负责通信的分别是:Native 的
JSCExecutor
与 JS 的MessageQueue
; - 在 Native 侧维护了一份曝露给 JS 的 module 注册表,在 JS 侧维护了一份曝露给 Native 的 module 注册表;
- RN 中 Native to JS 的通信没有使用
JavaScriptCore
提供的机制(block
、JSExport
),而是自己实现了一套跨平台通信机制。