本文通过分析源码,逐步解析 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
1 | void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) { |
在callFunction
方法中,首先判断环境是否准备好(第3
行),若尚未准备好,则进入bindBridge
1 | void JSCExecutor::bindBridge() throw(JSException) { |
bindBridge
方法主要完成一些初始化准备工作:从 JS 则获取batchedBridge
object 以及若干个方法(m_callFunctionReturnFlushedQueueJS
等)。
在BatchedBridge.js
中可以找到__fbBatchedBridge
的定义:
1 | const BatchedBridge = new MessageQueue(); |
可以看到,在 Native 里面拿到的batchedBridge
是一个MessageQueue
类型的 JS object。m_callFunctionReturnFlushedQueueJS
则是 JS 类MessageQueue
的callFunctionReturnFlushedQueue
方法。
回到JSCExecutor::callFunction
,在第6
行执行了callFunctionReturnFlushedQueue
方法。
MessageQueue.callFunctionReturnFlushedQueue(JS)
1 | callFunctionReturnFlushedQueue(module: string, method: string, args: Array<any>) { |
callFunctionReturnFlushedQueue
方法调用了内部的__callFunction
方法。
1 | __callFunction(module: string, method: string, args: Array<any>) { |
__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都需要注册。
1 | registerCallableModule(name: string, module: Object) { |
JS Module 注册表支持懒加载。
可通过registerCallableModule
或registerLazyCallableModule
接口注册。
如在RCTEventEmitter.js中注册的消息模块RCTEventEmitter
:
1 | BatchedBridge.registerCallableModule('RCTEventEmitter', eventEmitter); |
在InitializeCore.js中注册的基础模块(懒加载):
1 | BatchedBridge.registerLazyCallableModule('Systrace', () => require('Systrace')); |
至此,Native to JS 的流程基本结束。
but,事情并没有结束。
前面讲到,callFunctionReturnFlushedQueue
不会返回所调 JS 方法的返回值,但它确有返回值(从 JS 传给 Native):
1 | 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
),而是自己实现了一套跨平台通信机制。
Gitalking ...