准备-JavaScriptCore
在开篇,我们先简单准备下JavaScriptCore的知识。这是整个Native和JS沟通的最底层的桥梁。
Classes
JSManagedValue
主要用于防止Native导出对象时持有JSValue导致循环引用JSExport
导出Native到JavaScript
相对应的底层C实现
- JSBase
- JSContextRef
- JSObjectRef
- JSStringRef
- JSStringRefCF
- JSValueRef
ReactNative通信原理
从ReactNative的demo开始,入口我们只看到两个东西RCTRootView和RCTBridge。RCTRootView负责展示,RCTBridge则和ReactNative的通信有关。
入口
1 | - (BOOL)application:(__unused UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { |
RCTCxxBridge初始化
跟着调用链会看到,bridge的初始化会走到RCTCxxBridge中的start
1 | //RCTCxxBrige.mm |
start方法里做了这么几件事:
- 初始化native modules
- 初始化Instance->底层负责通信
- 加载本地js
要想了解native modules的初始化,我们要先看下之前的准备工作。
native modules导出模块
通过RCT_EXPORT_MODULE将本地的模块导出供JS使用
1 | //RCTBridgeModule.h |
最终导出的模块会被保存到RCTModuleClasses,使用时通过RCTGetModuleClasses()获取。RCTGetModuleClasses()就是在上面初始化native modules时用到的。
1 | //RCTBridge.m |
native modules导出方法
1 | //RCTBridgeModule.h |
将方法导出,最终生成以下方法提供给外部调用。通过遍历这个类中所有以
“__rct_export__“开头的方法就可以获取属于这个类的所有导出方法。
1 | //RCTBridgeModule.h |
生成RCTModuleData
初始化native modules的工作,其实就是根据之前导出的类和方法,生成对应的RCTModuleData对象。
1 | //RCTCxxBridge.mm |
至此,我们完成了本地模块和方法的导出,并且生成了一组RCTModuleData对象来表示他们。
初始化Instance
我们继续看Instance的初始化。
1 | // 初始化底层Instance |
初始化Instance需要一下几个元素:
InstanceCallback类型的回调,用于底层执行结束后往上层回调。
1
2
3
4
5
6struct InstanceCallback {
virtual ~InstanceCallback() {}
virtual void onBatchComplete() {}
virtual void incrementPendingJSCalls() {}
virtual void decrementPendingJSCalls() {}
};JSExecutorFactory类型的对象,用于生成JSExecutor用于真正执行JS。生产返回使用的是JSCExecutorFactory,返回JSIExecutor用于执行JS,调试使用的是RCTObjcExecutorFactory,返回RCTObjcExecutor通过websocket链接chrome执行JS。
1 | class JSExecutorFactory { |
- MessageQueueThread类型对象用于提供队列执行。这里是由RCTMessageThread来实现,内部用的是CFRunLoop来实现。
1 | //MessageQueueThread.h |
- ModuleRegistry,这个包含native module信息的对象,它的来源就是我们上面看到的RCTModuleData。可以看到最终透传参数生成了RCTNativeModule
1 | //RCTCxxBridge.mm |
有必要提一下,这上面的moduleData.instance,其实就是生成这个模块对应实例
1 | - (instancetype)initWithModuleClass:(Class)moduleClass |
同时也会准备好它所对应的bridge和method queue
1 | //RCTModuleData.mm |
同时我们看到Instance::initializeBridge中生成了NativeToJsBridge,到这里Instance的初始化就结束了,下面进入NativeToJsBridge。
NativeToJsBridge
NativeToJsBridge作用主要是桥接Native和JS,它包含几个关键属性
<JsToNativeBridge> m_delegate
JsToNativeBridge类型的引用,主要用于JS call Native<JSExecutor> m_executor
JSExecutor类型引用,主要用于执行Native call JS,这里实际使用是的是JSIExecutor(生产)/RCTObjcExecutor(调试)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19std::shared_ptr<JSExecutorFactory> executorFactory;
if (!self.executorClass) {
if ([self.delegate conformsToProtocol:@protocol(RCTCxxBridgeDelegate)]) {
id<RCTCxxBridgeDelegate> cxxDelegate = (id<RCTCxxBridgeDelegate>) self.delegate;
executorFactory = [cxxDelegate jsExecutorFactoryForBridge:self];
}
if (!executorFactory) {
//生产使用JSCExecutorFactory会生成JSIExecuror
executorFactory = std::make_shared<JSCExecutorFactory>();
}
} else {
//调试用RCTObjcExecutorFactory生成RCTObjcExecutor
id<RCTJavaScriptExecutor> objcExecutor = [self moduleForClass:self.executorClass];
executorFactory.reset(new RCTObjcExecutorFactory(objcExecutor, ^(NSError *error) {
if (error) {
[weakSelf handleError:error];
}
}));
}<MessageQueueThread> m_executorMessageQueueThread
MessageQueueThread类型引用,由上层传递,用于队列管理
JSIExecutor
JSIExecutor主要用来Native call JS,包含几个主要属性:
- <jsi::Runtime> runtime_
Runtime类型指针,代表JS的运行时。这是一个抽象类,其实际上是由JSCRuntime来实现的,JSCRuntime中的功能其实就是通过JavaScriptCode来完成(使用的C函数接口)。JSCRuntime上线了<jsi::Runtime>接口,提供了创建JS上下文的功能,同时可以执行JS。
1 | void JSCRuntime::evaluateJavaScript( |
<ExecutorDelegate> delegate_
ExecutorDelegate类型的指针,这里的ExecutorDelegate是抽象类,实际是由JsToNative来实现的。也即JSIExecutor引用了JsToNative。<JSINativeModules> nativeModules_
JSINativeModules由上层传入的ModuleRegistry构造而成,同时会将ModuleRegistry中包含的本地模块配置信息通过”__fbGenNativeModule”保存到JS端。
1 | //JSINativeModules.cpp |
genModule会根据ModuleRegistry生成的module和method信息生成JS端的方法,结构类似:
1 | { |
JSIExecutor执行js方法的实现值得说下。
1 | void JSIExecutor::callFunction( |
其中callFunctionReturnFlushedQueue_来自和JS端的属性绑定
1 | //JSIExecutor.cpp |
可以看到callFunctionReturnFlushedQueue_会调用JS端的callFunctionReturnFlushedQueue方法,最终调用在JS端注册好的JS模块和方法。
而getPropertyAsFunction则是通过runtime来实现。(runtime是JS和Native沟通的桥梁)
JsToNativeBridge
JsToNativeBridge的实现就简单很多,直接通过ModuleRegistry注册好的native信息,调用对应模块的对应方法。
1 | void callNativeModules( |
其中m_registry就是上层传入的ModuleRegistry对象
那JS Call Native的整套流程是怎样的呢?
- JS调用MessageQueue.enqueueNativeCall
1 | enqueueNativeCall( |
可以看到5ms刷新一次
- nativeFlushQueueImmediate对应本地的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//JSIExecutor.cpp
runtime_->global().setProperty(
*runtime_,
"nativeFlushQueueImmediate",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
1,
[this](
jsi::Runtime&,
const jsi::Value&,
const jsi::Value* args,
size_t count) {
if (count != 1) {
throw std::invalid_argument(
"nativeFlushQueueImmediate arg count must be 1");
}
callNativeModules(args[0], false);
return Value::undefined();
}));
可以看到runtime_获取到全局上下文(即JS端的global),将nativeFlushQueueImmediate属性关联到本地callNativeModules方法。
这里值得一提的是,runtime_->global().setProperty用在很多将JS属性和native方法对象等绑定。
- callNativeModules
1
2
3
4
5//JSIExecutor.cpp
void JSIExecutor::callNativeModules(const Value& queue, bool isEndOfBatch) {
delegate_->callNativeModules(
*this, dynamicFromValue(*runtime_, queue), isEndOfBatch);
}
delegate_就是我们上面提到的<ExecutorDelegate>类型指针,实际就是JsToNativeBridge,最终也就走到了我们上面说的callNativeModules。
到这里,我们已经具备了native call js和js call native的能力。