React Native和原生app通信机制详解
概述React Native用iOS自带的JavaScriptCore作为JS的解析引擎,但并没有用到JavaScriptCore提供的一些可以让JS与OC互调的特性,而是自己实现了一套机制,这套机制可以通用于所有JS引擎上,在没有JavaScriptCore的情况下也可以用webview代替,实际上项目里就已经有了用webview作为解析引擎的实现,应该是用于兼容iOS7以下没有JavascriptCore的版本。 //OC
@implement RCTSQLManager
- (void)query:(NSString *)queryData successCallback:(RCTResponseSenderBlOCk)responseSender
{
RCT_EXPORT();
NSString *ret = @"ret"
responseSender(ret);
}
@end
//JS:
RCTSQLManager.query("SELECT * FROM table",function(result) {
//result == "ret";
});
模块配置表首先OC要告诉JS它有什么模块,模块里有什么方法,JS才知道有这些方法后才有可能去调用这些方法。这里的实现是OC生成一份模块配置表传给JS,配置表里包括了所有模块和模块里方法的信息。例: {
"remoteModuleConfig": {
"RCTSQLManager": {
"methods": {
"query": {
"type": "remote","methodID": 0
}
},"moduleID": 4
},...
},}
OC端和JS端分别各有一个bridge,两个bridge都保存了同样一份模块配置表,JS调用OC模块方法时,通过bridge里的配置表把模块方法转为模块ID和方法ID传给OC,OC通过bridge的模块配置表找到对应的方法执行之,以上述代码为例,流程大概是这样(先不考虑callback): 1,取所有模块类每个模块类都实现了RCTBridgeModule接口,可以通过runtime接口objc_getClassList或objc_copyClassList取出项目里所有类,然后逐个判断是否实现了RCTBridgeModule接口,就可以找到所有模块类,实现在RCTBridgeModuleClassesByModuleID()方法里。 2,取模块里暴露给JS的方法一个模块里可以有很多方法,一些是可以暴露给JS直接调用的,一些是私有的不想暴露给JS,怎样做到提取这些暴露的方法呢?我能想到的方法是对要暴露的方法名制定一些规则,比如用RCTExport_作为前缀,然后用runtime方法class_getInstanceMethod取出所有方法名字,提取以RCTExport_为前缀的方法,但这样做恶心的地方是每个方法必须加前缀。React Native用了另一种黑魔法似的方法解决这个问题:编译属性attribute。 #define RCT_EXPORT(JS_name) __attribute__((used,section("__DATA,RCTExport"
))) static const char *__rct_export_entry__[] = { __func__,#JS_name }
这个宏的作用是用编译属性attribute给二进制文件新建一个section,属于DATA数据段,名字为RCTExport,并在这个段里加入当前方法名。编译器在编译时会找到__attribute进行处理,为生成的可执行文件加入相应的内容。效果可以从linkmap看出来: # Sections:
# Address Size Segment Section
0x100001670 0x000C0180 __TEXT __text
...
0x10011EFA0 0x00000330 __DATA RCTExport
0x10011F2D0 0x00000010 __DATA __common
0x10011F2E0 0x000003B8 __DATA __bss
...
0x10011EFA0 0x00000010 [ 4] -[RCTStatusBarManager setStyle:animated:].__rct_export_entry__
0x10011EFB0 0x00000010 [ 4] -[RCTStatusBarManager setHidden:withAnimation:].__rct_export_entry__
0x10011EFC0 0x00000010 [ 5] -[RCTSourceCode getScriptText:failureCallback:].__rct_export_entry__
0x10011EFD0 0x00000010 [ 7] -[RCTAlertManager alertWithArgs:callback:].__rct_export_entry__
...
可以看到可执行文件数据段多了个RCTExport段,内容就是各个要暴露给JS的方法。这些内容是可以在运行时获取到的,在RCTBridge.m的RCTExportedMethodsByModuleID()方法里获取这些内容,提取每个方法的类名和方法名,就完成了提取模块里暴露给JS方法的工作。整体的模块类/方法提取实现在RCTRemoteModulesConfig()方法里。 js和oc的调用流程分析接下来看看JS调用OC模块方法的详细流程,包括callback回调。
问题剖析上述第4步留下一个问题,JS是怎样把数据传给OC,让OC去调相应方法的? 答案是通过返回值。JS不会主动传递数据给OC,在调OC方法时,会在上述第4步把ModuleID,MethodID等数据加到一个队列里,等OC过来调JS的任意方法时,再把这个队列返回给OC,此时OC再执行这个队列里要调用的方法。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |