initModules

该职分会扫描全部的 Native 模块,提收取要暴露给 JS
的这个模块,然后保留到一个字典对象中。
二个 Native 模块若是想要揭穿给 JS,要求在宣称时显得地调用
RCT_EXPORT_MODULE。它的概念如下:

#define RCT_EXPORT_MODULE(js_name) \ RCT_EXTERN void
RCTRegisterModule(Class); \ + (NSString *)moduleName { return
@#js_name; } \ + (void)load { RCTRegisterModule(self); }

1
2
3
4
#define RCT_EXPORT_MODULE(js_name) \
RCT_EXTERN void RCTRegisterModule(Class); \
+ (NSString *)moduleName { return @#js_name; } \
+ (void)load { RCTRegisterModule(self); }

能够见到,这正是三个宏,定义了 load
方法,该方法会自动被调用,在艺术中对当前类举行挂号。

模块尽管要暴露出钦命的章程,供给通过 RCT_EXPORT_METHOD
宏进行宣示,原理类似。

总结

俗话说一图胜千言,整个运转进程用一张图回顾起来正是:
澳门凯旋门注册网址 1

本文简单介绍了 iOS 端运维时 JS 和 Native 的并行进度,能够见见
BatchedBridge 在双方通信进程中扮演了主要的剧中人物。Native 调用 JS 是经过
WebSocket 或直接在 javascriptcore 引擎上奉行;JS 调用 Native
则只把调用的模块、方法和参数先缓存起来,等到事件触发后通过重临值传到
Native 端,其余两个都封存了具有揭发的 Native
模块新闻表作为通讯的底蕴。由于对 iOS
端开辟并不熟悉,文中如有错误的地方还请建议。

参考资料:

  • GCD
    Reference
  • BRIDGING IN REACT
    NATIVE
  • React Native
    实验斟酌报告
  • 凯旋门074网址,React Native通讯机制详解

    1 赞 1 收藏
    评论

澳门凯旋门注册网址 2

JS 运营进度

React Native 的 iOS 端代码是一向从 Xcode IDE
里运维的。在运行时,首先要对代码进行编写翻译,不出意外,在编写翻译后会弹出二个指令行窗口,这一个窗口正是通过
Node.js 运维的 development server

主题素材是其一命令行是怎么运行起来的吧?实际上,Xcode 在 Build Phase
的末段叁个阶段对此做了安顿:
澳门凯旋门注册网址 3

于是,代码编写翻译后,就能实行 packager/react-native-xcode.sh 那几个剧本。
翻看这几个本子中的内容,开采它首固然读取 XCode 带过来的景况变量,同一时候加载
nvm 包使得 Node.js 景况可用,最终施行 react-native-cli 的通令:

react-native bundle \ –entry-file index.ios.js \ –platform ios \
–dev $DEV \ –bundle-output “$DEST/main.jsbundle” \ –assets-dest
“$DEST”

1
2
3
4
5
6
react-native bundle \
  –entry-file index.ios.js \
  –platform ios \
  –dev $DEV \
  –bundle-output "$DEST/main.jsbundle" \
  –assets-dest "$DEST"

react-native 命令是大局安装的,在自家本机上它的地方是
/usr/local/bin/react-native。查看该文件,它调用了 react-native
包里的local-cli/cli.js 中的 run 方法,最后进入了
private-cli/src/bundle/buildBundle.js。它的调用进度为:

  1. ReactPackager.createClientFor
  2. client.buildBundle
  3. processBundle
  4. saveBundleAndMap

地方四步成功的是 buildBundle
的功用,细节繁多很复杂。总体来讲,buildBundle 的作用周边于 browerify 或
webpack :

  1. 从进口文件初叶解析模块之间的信赖性关系;
  2. 对 JS 文件转载,举个例子 JSX 语法的转向等;
  3. 澳门凯旋门游戏网址,澳门凯旋门注册网址,把转化后的逐条模块一同联合为三个 bundle.js

因此 React Native 单独去落到实处这几个包裹的进程,而不是直接使用 webpack
,是因为它对模块的深入分析和编译做了好些个优化,大大升高了包装的进程,那样能够确认保障在
liveReload 时用户及时获得响应。

Tips: 通过拜访
可以看出内部存储器中缓存的具有编写翻译后的文件名及文件内容,如:
澳门凯旋门注册网址 4

loadSource

该任务担任加载 JS 代码到内部存款和储蓄器中。和前边一致,借使 JS 地址是 UCRUISERL
的花样,就透过网络去读取,假若是文本的款式,则透过读本地球磁性盘文件的方式读取。

moduleConfig

据书上说保存的模块音讯,组装成一个 JSON ,对应的字段为 remoteModuleConfig。

JS 调用 Native

近年来大家见到, Native 调用 JS 是通过发送音信到 Chrome
触发推行、或然直接通过 javascriptcore 施行 JS 代码的。而对此 JS 调用
Native 的景况,又是怎么样的吗?

在 JS 端调用 Native 一般都以一向通过引用模块名,然后就动用了,比方:

JavaScript

var RCTAlertManager = require(‘NativeModules’).AlertManager

1
var RCTAlertManager = require(‘NativeModules’).AlertManager

可知,NativeModules 是持有地点模块的操作接口,找到它的定义为:

JavaScript

var NativeModules = require(‘BatchedBridge’).RemoteModules;

1
var NativeModules = require(‘BatchedBridge’).RemoteModules;

而BatchedBridge中是八个MessageQueue的指标:

JavaScript

let BatchedBridge = new MessageQueue(
__fbBatchedBridgeConfig.remoteModuleConfig,
__fbBatchedBridgeConfig.localModulesConfig, );

1
2
3
4
let BatchedBridge = new MessageQueue(
  __fbBatchedBridgeConfig.remoteModuleConfig,
  __fbBatchedBridgeConfig.localModulesConfig,
);

在 MessageQueue 实例中,都有贰个 RemoteModules 字段。在 MessageQueue
的构造函数中能够看到,RemoteModules 正是
__fbBatchedBridgeConfig.remoteModuleConfig 稍微加工后的结果。

JavaScript

class MessageQueue { constructor(remoteModules, localModules,
customRequire) { this.RemoteModules = {};
this._genModules(remoteModules); … } }

1
2
3
4
5
6
7
8
class MessageQueue {
 
  constructor(remoteModules, localModules, customRequire) {
    this.RemoteModules = {};
    this._genModules(remoteModules);
    …
    }
}

之所以难题就成为: __fbBatchedBridgeConfig.remoteModuleConfig
是在哪个地方赋值的?

事实上,这一个值正是 从 Native 端传过来的JSON 。如前所述,Executor
会把模块配置组装的 JSON 保存到内部:

JavaScript

[_javaScriptExecutor injectJSONText:configJSON
asGlobalObjectNamed:@”__fbBatchedBridgeConfig” callback:onComplete];

1
2
3
[_javaScriptExecutor injectJSONText:configJSON
                  asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
                             callback:onComplete];

configJSON
实际保存的字段为:_injectedObjects['__fbBatchedBridgeConfig']

在 Native 第一遍调用 JS 时,_injectedObjects 会作为传递音讯的
inject 字段。
JS 端收到这几个音讯,经过上面这么些重大的管理进程:

JavaScript

‘executeApplicationScript’: function(message, sendReply) { for (var key
in message.inject) { self[key] = JSON.parse(message.inject[key]); }
importScripts(message.url); sendReply(); },

1
2
3
4
5
6
7
‘executeApplicationScript’: function(message, sendReply) {
    for (var key in message.inject) {
      self[key] = JSON.parse(message.inject[key]);
    }
    importScripts(message.url);
    sendReply();
  },

看来没,这里读取了 inject 字段并打开了赋值。self
是二个大局的命名空间,在浏览器里 self===window

由此,上面代码施行过后,window.__fbBatchedBridgeConfig
就被赋值为了传过来的 JSON 反类别化后的值。

总之:
NativeModules = __fbBatchedBridgeConfig.remoteModuleConfig =
JSON.parse(message.inject[‘__fbBatchedBridgeConfig’]) =
模块暴表露的具有音信

好,有了上述的前提之后,接下去以一个其实调用例子表达下 JS 调用 Native
的进度。
率先咱们经过 JS 调用一个 Native 的点子:

JavaScript

‘executeApplicationScript’: function(message, sendReply) { for (var key
in message.inject) { self[key] = JSON.parse(message.inject[key]); }
importScripts(message.url); sendReply(); },

1
2
3
4
5
6
7
‘executeApplicationScript’: function(message, sendReply) {
    for (var key in message.inject) {
      self[key] = JSON.parse(message.inject[key]);
    }
    importScripts(message.url);
    sendReply();
  },

本篇小说对 iOS 端 React Native。不无 Native 方法调用时都会先进入到下边的章程中:

JavaScript

fn = function(…args) { let lastArg = args.length > 0 ?
args[本篇小说对 iOS 端 React Native。args.length – 1] : null; let secondLastArg = args.length > 1 ?
args[args.length – 2] : null; let hasSuccCB = typeof lastArg ===
‘function’; let hasErrorCB = typeof secondLastArg === ‘function’; let
numCBs = hasSuccCB + hasErrorCB; let onSucc = hasSuccCB ? lastArg :
null; let onFail = hasErrorCB ? secondLastArg : null; args =
args.slice(0, args.length – numCBs); return self.__nativeCall(module,
method, args, onFail, onSucc); };

1
2
3
4
5
6
7
8
9
10
11
fn = function(…args) {
  let lastArg = args.length > 0 ? args[args.length – 1] : null;
  let secondLastArg = args.length > 1 ? args[args.length – 2] : null;
  let hasSuccCB = typeof lastArg === ‘function’;
  let hasErrorCB = typeof secondLastArg === ‘function’;
  let numCBs = hasSuccCB + hasErrorCB;
  let onSucc = hasSuccCB ? lastArg : null;
  let onFail = hasErrorCB ? secondLastArg : null;
  args = args.slice(0, args.length – numCBs);
  return self.__nativeCall(module, method, args, onFail, onSucc);
};

相当于尾数后三个参数是不当和不错的回调,剩下的是办法调用自己的参数。

在 __nativeCall 方法中,会将七个回调压到 callback 数组中,同时把
(模块、方法、参数) 也单身保存到个中的行列数组中:

JavaScript

onFail && params.push(this._callbackID);
this._callbacks[this._callbackID++] = onFail; onSucc &&
params.push(this._本篇小说对 iOS 端 React Native。callbackID); this._callbacks[this._callbackID++]
= onSucc; this._queue[0].push(module);
this._queue[1].push(method); this._queue[2]本篇小说对 iOS 端 React Native。.push(params);

1
2
3
4
5
6
7
onFail && params.push(this._callbackID);
this._callbacks[this._callbackID++] = onFail;
onSucc && params.push(this._callbackID);
this._callbacks[this._callbackID++] = onSucc;
this._queue[0].push(module);
this._queue[1].push(method);
this._queue[2].push(params);

到这一步,JS 端告一段落。接下来是 Native 端,在调用 JS
时,经过如下的流水生产线:

澳门凯旋门注册网址 5

一句话来讲,正是在调用 JS 时,顺便把前边封存的 queue 用作再次回到值
一并回到,然后会对该重临值进行分析。
在 _handleRequestNumber 方法中,终于变成了 Native 方法的调用:

– (BOOL)_handleRequestNumber:(NSUInteger)i
moduleID:(NSUInteger)moduleID methodID:(NSUInteger)methodID
params:(NSArray *)params { // 深入分析模块和格局 RCTModuleData *moduleData
= _moduleDataByID[moduleID]; id<RCTBridgeMethod> method =
moduleData.methods[methodID]; <a
href=’; {
// 完毕调用 [method invokeWithBridge:self module:moduleData.instance
arguments:params]; } <a
href=’;
(NSException *exception) { } NSMutableDictionary *args =
[method.profileArgs mutableCopy]; [args setValue:method.JSMethodName
forKey:@”method”]; [args
setValue:RCTJSONStringify(RCTNullIfNil(params), NULL) forKey:@”args”];
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
– (BOOL)_handleRequestNumber:(NSUInteger)i
                    moduleID:(NSUInteger)moduleID
                    methodID:(NSUInteger)methodID
                      params:(NSArray *)params
{
  // 解析模块和方法
  RCTModuleData *moduleData = _moduleDataByID[moduleID];
  id<RCTBridgeMethod> method = moduleData.methods[methodID];
  <a href=’http://www.jobbole.com/members/xyz937134366′>@try</a> {
    // 完成调用
    [method invokeWithBridge:self module:moduleData.instance arguments:params];
  }
  <a href=’http://www.jobbole.com/members/wx895846013′>@catch</a> (NSException *exception) {
  }
 
  NSMutableDictionary *args = [method.profileArgs mutableCopy];
  [args setValue:method.JSMethodName forKey:@"method"];
  [args setValue:RCTJSONStringify(RCTNullIfNil(params), NULL) forKey:@"args"];
}

再正是,实践后还有或许会透过 invokeCallbackAndReturnFlushedQueue 触发 JS
端的回调。具体细节在 RCTModuleMethod 的 processMethodSignature 方法中。

再下结论一下,JS 调用 Native 的进度为 :

  • JS 把(调用模块、调用方法、调用参数) 保存到行列中;
  • Native 调用 JS 时,顺便把队列重临过来;
  • Native 管理队列中的参数,一样深入分析出(模块、方法、参数),并透过
    NSInvocation 动态调用;
  • Native方法调用完结后,再度主动调用 JS。JS 端通过
    callbackID,找到呼应JS端的 callback,进行一次调用

成套进程大约正是那般,剩下的八个难点不怕,为何要等待 Native 调用 JS
时才会接触,中间会不会有相当长延时?
实际上,只要有事件触发,Native 就能够调用
JS。举个例子,用户假设对显示器进行触摸,就能触发在 RCTRootView 中注册的
Handler,并发送给JS:

JavaScript

[_bridge enqueueJSCall:@”RCTEventEmitter.receiveTouches”
args:@[eventName, reactTouches, changedIndexes]];

1
2
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveTouches"
                  args:@[eventName, reactTouches, changedIndexes]];

除了触摸事件,还会有 Timer 事件,系统事件等,只要事件触发了,JS
调用时就能够把队列再次来到。那块明白能够参照 React
Native通讯机制详解
一文中的“事件响应”一节。

injectJSONConfiguration

该职责将上二个职责创建的 JSON 注入到 Executor 中。
下边是一个 JSON 示例,由于实在的对象太大,这里只截取了前头的某些:
澳门凯旋门注册网址 6
JSON 里面就是具备暴流露来的模块音讯。

选拔 JS 创设跨平台的原生应用:React Native iOS 通讯机制初探

2015/12/30 · JavaScript
· React Native

初稿出处: Taobao前端团队(FED)-
乾秋   

澳门凯旋门注册网址 7

在初识 React Native 时,特别令人思疑的多个地点便是 JS 和 Native
两个端之间是哪些彼此通讯的。本篇作品对 iOS 端 React Native
运维时的调用流程做下简要总计,以此窥探其幕后的通讯机制。

executeSourceCode

该职分中会推行加载过来的 JS 代码,执行时传出在此以前注入的 JSON。
在调节和测试形式下,会透过 WebSocket 给 Chrome 发送一条 message,内容大约为:

JavaScript

{ id = 10305; inject = {remoteJSONConfig…}; method =
executeApplicationScript; url =
“”; }

1
2
3
4
5
6
{
    id = 10305;
    inject = {remoteJSONConfig…};
    method = executeApplicationScript;
    url = "http://localhost:8081/index.ios.bundle?platform=ios&dev=true";
}

JS 接收音讯后,实施打包后的代码。若是是非调节和测试情势,则一直通过
javascriptcore 的虚拟意况去执行相关代码,效果类似。

setupExecutor

这里设置的是 JS 引擎,同样分为调节和测试情状和生育碰着:
在调节和测试情形下,对应的 Executor 为 RCTWebSocketExecutor,它通过 WebSocket
连接受 Chrome 中,在 Chrome 里运维 JS;
在生养境况下,对应的 Executor 为 RCTContextExecutor,那应当正是风传中的
javascriptcore

Native 运维进度

Native 端便是三个 iOS 程序,程序入口是 main
函数,像普通同样,它承担对应用程序做伊始化。

除了那一个之外 main 函数之外,AppDelegate
也是二个比较首要的类,它根本用于做一些大局的主宰。在应用程序运营以往,在这之中的
didFinishLaunchingWithOptions
方法会被调用,在这几个措施中,重要做了几件事:

  • 概念了 JS 代码所在的岗位,它在 dev 意况下是三个 U福睿斯L,通过
    development server
    访问;在生产条件下则从磁盘读取,当然前提是早就手动生成过了 bundle
    文件;
  • 始建了贰个 RCTRootView 对象,该类承袭于 UIView,处于程序有所
    View 的最外层;
  • 调用 RCTRootView 的 initWithBundleURL 方法。在该办法中,创设了
    bridge 对象。循名责实,bridge
    起着三个端之间的桥接功用,在那之中的确行事的是类正是出名的
    RCTBatchedBridge

RCTBatchedBridge 是初阶化时通讯的骨干,大家爱抚关怀的是 start 方法。在
start 方法中,会创设一个 GCD
线程,该线程通过串行队列调整了以下多少个基本点的职分。

相关文章