准备-JavaScriptCore
在开篇,我们先简单准备下JavaScriptCore的知识。这是整个Native和JS沟通的最底层的桥梁。
Classes
JSContext
JS的执行上下文JSManagedValue
主要用于防止Native导出对象时持有JSValue导致循环引用JSValue
JavaScript值的引用,转换JavaScript和Native之间的基本数据JSVirtualMachine
提供JS执行环境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中的start1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36//RCTCxxBrige.mm
//只贴了关键代码
- (void)start {
dispatch_group_t prepareBridge = dispatch_group_create();
// 初始化native modules
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 初始化底层Instance
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 加载js代码
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
}
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
} onProgress:^(RCTLoadingProgress *progressData) {
}];
// 执行js代码
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
}
start方法里做了这么几件事:
- 初始化native modules
- 初始化Instance->底层负责通信
- 加载本地js
要想了解native modules的初始化,我们要先看下之前的准备工作。
native modules导出模块
通过RCT_EXPORT_MODULE将本地的模块导出供JS使用
1 | //RCTBridgeModule.h |
最终导出的模块会被保存到RCTModuleClasses,使用时通过RCTGetModuleClasses()获取。RCTGetModuleClasses()就是在上面初始化native modules时用到的。1
2
3
4
5
6
7
8//RCTBridge.m
NSArray<Class> *RCTGetModuleClasses(void) {
__block NSArray<Class> *result;
dispatch_sync(RCTModuleClassesSyncQueue, ^{
result = [RCTModuleClasses copy];
});
return result;
}
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
2
3
4
5
6
7- (instancetype)initWithModuleClass:(Class)moduleClass
bridge:(RCTBridge *)bridge
{
return [self initWithModuleClass:moduleClass
moduleProvider:^id<RCTBridgeModule>{ return [moduleClass new]; }
bridge:bridge];
}
同时也会准备好它所对应的bridge和method queue1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 //RCTModuleData.mm
- (void)setBridgeForInstance
{
if ([_instance respondsToSelector:@selector(bridge)] && _instance.bridge != _bridge) {
@try {
[(id)_instance setValue:_bridge forKey:@"bridge"];
}
@catch (NSException *exception) {
//...
}
}
}
- (void)setUpMethodQueue
{
if (_instance && !_methodQueue && _bridge.valid) {
BOOL implementsMethodQueue = [_instance respondsToSelector:@selector(methodQueue)];
if (implementsMethodQueue && _bridge.valid) {
_methodQueue = _instance.methodQueue;
}
if (!_methodQueue && _bridge.valid) {
_queueName = [NSString stringWithFormat:@"com.facebook.react.%@Queue", self.name];
_methodQueue = dispatch_queue_create(_queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
// assign it to the module
if (implementsMethodQueue) {
@try {
[(id)_instance setValue:_methodQueue forKey:@"methodQueue"];
}
@catch (NSException *exception) {
//...
}
}
}
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}
}
同时我们看到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
2
3
4
5
6{
name: moduleName,
module: {
methodName: func
}
}
JSIExecutor执行js方法的实现值得说下。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33void JSIExecutor::callFunction(
const std::string& moduleId,
const std::string& methodId,
const folly::dynamic& arguments) {
if (!callFunctionReturnFlushedQueue_) {
bindBridge();
}
auto errorProducer = [=] {
std::stringstream ss;
ss << "moduleID: " << moduleId << " methodID: " << methodId
<< " arguments: " << folly::toJson(arguments);
return ss.str();
};
Value ret = Value::undefined();
try {
scopedTimeoutInvoker_(
[&] {
ret = callFunctionReturnFlushedQueue_->call(
*runtime_,
moduleId,
methodId,
valueFromDynamic(*runtime_, arguments));
},
std::move(errorProducer));
} catch (...) {
std::throw_with_nested(
std::runtime_error("Error calling " + moduleId + "." + methodId));
}
callNativeModules(ret, true);
}
其中callFunctionReturnFlushedQueue_来自和JS端的属性绑定1
2
3
4
5
6
7
8
9
10
11
12
13//JSIExecutor.cpp
Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
*runtime_, "callFunctionReturnFlushedQueue");
//MessageQueue.js
callFunctionReturnFlushedQueue(module: string, method: string, args: any[]) {
this.__guard(() => {
this.__callFunction(module, method, args);
});
return this.flushedQueue();
}
可以看到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.enqueueNativeCall1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22enqueueNativeCall(
moduleID: number,
methodID: number,
params: any[],
onFail: ?Function,
onSucc: ?Function,
) {
this._queue[MODULE_IDS].push(moduleID);
this._queue[METHOD_IDS].push(methodID);
this._queue[PARAMS].push(params);
const now = Date.now();
//MIN_TIME_BETWEEN_FLUSHES_MS = 5
if (
global.nativeFlushQueueImmediate &&
now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS
) {
const queue = this._queue;
this._queue = [[], [], [], this._callID];
this._lastFlush = now;
global.nativeFlushQueueImmediate(queue);
}
}
可以看到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的能力。