部分摘自 React-Native 入门与实战
0x01 通信机制
React-Native(之后简称RN)作为一个JavaScript跨终端框架,与终端原生语言之间的交互是必不可少的。在RN推出之前,如果想用JavaScript来开发,通常会选择Hybrid,同样提供了JavaScript类库来调用原生的设备功能,这类应用使用UIWebVIew作为主要界面,通过向WebView请求加载特殊的URL的方式来调用特殊的原生功能,最后调用UIWebView的stringByEaluatingJavaScriptFromString接口执行JavaScript脚本传递数据。RN中的交互与Hybrid有着相似之处,RN是通过调用JavaScriptCore框架提供的JavaScript运行环境,通过调用evaluateScript方法执行特定的JavaScript脚本来传递数据,完成JS与native之间的交互(这里特指iOS android有另外方法,文后所有方法都特指RN在iOS中的运用 不再赘述)。与Hybrid相比,RN中JavaScript构建的界面完全基于原生组件,原生界面的用户体验优于webView则是毫无疑问的。
0x01-1 模块配置映射
任何不同语言之间的交互及调用不外乎之间存在的某种映射关系,以至于我们能准确的调用某个特定的方法,传入特定的参数。RN也如此,当JavaScript调用native模块时,必须包含模块、方法和参数的信息,从而定位具体调用了哪个模块的哪个方法。此时就需要之前提到的一个模块的配置信息映射表,来作为双方通信的桥梁。now,RN是如何提供这个模块配置映射表的呢。
在RN加载的时候,所有注册并且符合规范的模块都会被导出,生成相应的模块数据类RCTModuleData,而模块数据中缓存了模块的对象实例,以及模块索引ModuleID。
在RCTModuleData模块数据中,当前模块所有的模块方法都会通过OC运行时方法被导出,生成模块方法类RCTModuleMethod。而RCTModuleMethod对象缓存了原生方法与JavaScript方法的对应关系以及模块的实例,并且在生成配置的时候会顺序生成索引的MethodID。在此之后,回味JavaScript模块生成一份模块配置表,其中包含模块名,JavaScript方法,常量参数,moduleID,moduleMethod。最终,这份配置信息会通过[_javaScriptExecutor injectJSONText:asGlobalObjectNamed: callback:]向JavaScript端注册,生成对应的JavaScript模块对象和方法。
0x01-2 通信流程
JavaScript 就是如上图的流程通过配置信息完成与Native模块的相互调用。
JavaScript会维护一个MessageQueue。当调用模块的方法时,调用会被解析为模块名ModuleName。模块方法MethodName、参数Params三个部分,并将这三个部分传递给MessageQueue,通过前面生成的模块配置remoteModuleConfig解析为ModuleID、MethodID。
MessageQueue是一个很重要的模块,所起的作用与原生代码中的RCTBridge类似,都提供了将模块方法调用与模块通信数据相互转换的功能。同时,MessageQueue提供了4个接口,RN就是通过在RCTBridge调用特定的接口来完成JavaScript与Native之间的通信。
processBatch获得JavaScript模块的方法调用。
invokeCallbackAndReturnFlushedQueue调用JavaScript模块执行时传递的回调函数。
callFunctionReturnFlushedQueue处理Objective-C对JavaScript模块的功能调用。
flushedQueue用于刷新MessageQueue队列
如果调用参数中包含了回调函数,MessageQueue会将参数Params中的回调函数缓存在本地,并生成一个CallbackID来代替。最终将JavaScript调用以ModuleID、MethodID、Params的形式缓存起来传递给Native。注意这里并不是由JavaScript模块主动将数据产地给Native模块的,而是由Native模块主动调用MessageQueue的接口(processBatch),该接口的返回值为MessageQueue队列中缓存的JavaScript调用的消息数据([ModuleID,MethodID,Params])。
Native获得信息数据后,通过ModuleID索引到对应模块对象RCTModuleData,然后在模块对象中通过MethodID索引到模块方法RCTModuleMethod,这样就定位到调用JavaScript模块方法所对应的Native模块方法。