来声明该模块需要暴露给,betway体育app:Native层

总结

民间语说生机勃勃图胜千言,整个运营进度用一张图回顾起来就是:
betway体育app 1

本文简单介绍了 iOS 端运行时 JS 和 Native 的互相进度,能够见到BatchedBridge 在双边通讯进度中扮演了举足轻重的剧中人物。Native 调用 JS 是因此WebSocket 或直接在 javascriptcore 引擎上进行;JS 调用 Native 则只把调用的模块、方法和参数先缓存起来,等到事件触发后透过重临值传到 Native 端,此外两个都封存了全数暴露的 Native 模块新闻表作为通讯的底子。由于对 iOS 端开辟并不熟知,文中如有错误的地点还请提议。

参照他事他说加以考察资料:

  • GCD Reference
  • BRIDGING IN REACT NATIVE
  • React Native 调查商讨报告
  • React Native通讯机制详整

    1 赞 1 收藏 评论

betway体育app 2

身体力行工程的代码

 render: function() { return ( <View style={styles.container}> <Text style={styles.welcome}> Welcome to React Native! </Text> <Text style={styles.instructions}> {this.state.changeText} </Text> <Text style={styles.welcome} onPress={this._onPress}> Change </Text> </View> ); },

betway体育app 311.png

)

[Native] 注册 Native Module(在逐风度翩翩 Native Module 的概念中)

在 Native 端实现模块时都亟待调用 RCT_EXPORT_MODULE 来注明该模块须要暴光给 JavaScript。除了那几个之外还应该有一堆宏定义用来声称别的的音讯:

  • RCT_EXPORT_MODULE:评释模块
  • RCT_EXPORT_METHOD:表明方法
  • RCT_EXPORT_VIEW_PROPERTY:注解属性

经过这种办法成就模块定义后,就足以充裕有利的拿走到独具供给对外揭发的模块以至模块必要对外暴光的不二秘诀与品质。

以下是着力的宏定义:

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

#define RCT_EXPORT_VIEW_PROPERTY(name, type) 
+ (NSArray<NSString *> *)propConfig_##name { return @[@#type]; }

...

4.总结

Java调用JavaScript能够总括为上面:

betway体育app 4

调用流程.png

应接关心民众号:JueCode

else if (self.bundleURL) {    [RCTJavaScriptLoaderloadBundleAtURL:self.bundleURLonComplete:onSourceLoad];  }

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 宏实行宣示,原理相同。

线程难点

React Native为JS引擎创立了多个单身的线程

//RCTJavaScriptContext- (instancetype)init{ NSThread *javaScriptThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoopThread) object:nil]; javaScriptThread.name = @"com.facebook.React.JavaScript"; [javaScriptThread start]; return [self initWithJavaScriptThread:javaScriptThread context:nil];}

怀有的JS代码都运营在"com.facebook.React.JavaScript"后台线程中,全体的操作都以异步,不会卡死主线程UI。並且JS调用到Native中的接口中有强制的线程检查,假诺不是在React线程中则抛出非常。那样有叁个主题材料,从JS调用Native中的代码是施行在这里个后台线程中,大家上文的RCTAlertManager.alertWithArgs显明是个操作UI的接口,施行在后台线程会crash,在导出RCTAlertManager时,通过兑现形式- (dispatch_queue_t卡塔尔(قطر‎methodQueue,原生模块能够钦定自个儿想在哪些队列中被实行

- (dispatch_queue_t)methodQueue{ return dispatch_get_main_queue();}

临近的,要是二个操作供给花费相当长日子,原生模块不应有窒碍住,而是应该声美素佳儿个用于施行操作的独立队列。比方,RCTAsyncLocalStorage模块成立了和睦的多个queue,那样它在做一些极慢的磁盘操作的时候就不会窒碍住React自个儿的新闻队列:

- (dispatch_queue_t)methodQueue{ return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);}

该体系作品希望从非 UI 模块以致 UI 模块的初阶化与通讯来剖析 React Native 一些完结原理。那篇文章是该种类的首先篇,首要分析了非 UI 模块从 APP 运行、Native 端的模块定义到 JavaScript 端的模块生成进程(基于 React Native @0.29.0 iOS 实现)。

同伙们都精晓在Android开垦中得以实现Java和JS的通信能够因此WebView来完成,包含注册JSBridge可能在接口中阻止都能够。但是React Native中并不曾用WebView控件的点子,而是依据WebKit内核的主意来落实Java与JS的通信,也正是Java与JS之间的通讯都是通过中间层C++来达成的,前日大家来分析下Android中React Native怎么落实Java和JS之间的通讯。

3、Module Queue:各类module都有二个queue,串行实行。但是为啥要在每种module本人的queue来实行?

使用 JS 营造跨平台的原生应用:React Native iOS 通信机制初探

2015/12/30 · JavaScript · React Native

原稿出处: Taobao前端共青团和少先队(FED)- 乾秋   

betway体育app 5

在初识 React Native 时,非常令人纠葛的贰个地点就是 JS 和 Native 三个端之间是如何互相通讯的。本篇文章对 iOS 端 React Native 运维时的调用流程做下简要总结,以此眼线其幕后的通讯机制。

App运维进度中 Native和JS互相调用的日志

[Log] N->JS : RCTDeviceEventEmitter.emit(["appStateDidChange",{"app_state":"active"}]) (main.js, line 638)[Log] N->JS : RCTDeviceEventEmitter.emit(["networkStatusDidChange",{"network_info":"wifi"}]) (main.js, line 638)[Log] N->JS : AppRegistry.runApplication(["MGReactNative",{"rootTag":1,"initialProps":{}}]) (main.js, line 638)[Log] Running application "MGReactNative" with appParams: {"rootTag":1,"initialProps":{}}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF (main.js, line 638)[Log] JS->N : RCTUIManager.createView([2,"RCTView",1,{"flex":1}]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([3,"RCTView",1,{"flex":1}]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([4,"RCTView",1,{"flex":1,"justifyContent":"center","alignItems":"center","backgroundColor":4294311167}]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([5,"RCTText",1,{"fontSize":20,"textAlign":"center","margin":10,"accessible":true,"allowFontScaling":true}]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([6,"RCTRawText",1,{"text":"Welcome to React Native!"}]) (main.js, line 638)[Log] JS->N : RCTUIManager.manageChildren([5,null,null,[6],[0],null]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([7,"RCTText",1,{"textAlign":"center","color":4281545523,"marginBottom":5,"accessible":true,"allowFontScaling":true}]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([8,"RCTRawText",1,{"text":"soap1"}]) (main.js, line 638)[Log] JS->N : RCTUIManager.manageChildren([7,null,null,[8],[0],null]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([9,"RCTText",1,{"fontSize":20,"textAlign":"center","margin":10,"accessible":true,"allowFontScaling":true,"isHighlighted":false}]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([10,"RCTRawText",1,{"text":"Change"}]) (main.js, line 638)[Log] JS->N : RCTUIManager.manageChildren([9,null,null,[10],[0],null]) (main.js, line 638)[Log] JS->N : RCTUIManager.manageChildren([4,null,null,[5,7,9],[0,1,2],null]) (main.js, line 638)[Log] JS->N : RCTUIManager.manageChildren([3,null,null,[4],[0],null]) (main.js, line 638)[Log] JS->N : RCTUIManager.createView([12,"RCTView",1,{"position":"absolute"}]) (main.js, line 638)[Log] JS->N : RCTUIManager.manageChildren([2,null,null,[3,12],[0,1],null]) (main.js, line 638)[Log] JS->N : RCTUIManager.manageChildren([1,null,null,[2],[0],null]) (main.js, line 638)

日记展现了运转React Native 界面Native与JS的调用进度,大家从最简便易行的例证动手,稳步脱下美丽的女人的面纱。

启动 JavaScript App

- (void)runApplication:(RCTBridge *)bridge
{
  NSString *moduleName = _moduleName ?: @"";
  NSDictionary *appParameters = @{
    @"rootTag": _contentView.reactTag,
    @"initialProps": _appProperties ?: @{},
  };

  [bridge enqueueJSCall:@"AppRegistry.runApplication"
                   args:@[moduleName, appParameters]];
}

Java要能调用到JS必要在Java层注册JS模块,先来看下JS模块的注册进程。

3、扩张工夫:JS怎样调用EscortN没有的组件?

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();
  },

怀有 Native 方法调用时都会先步向到上面包车型客车主意中:

JavaScript

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); };

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._callbackID); this._callbacks[this._callbackID++] = onSucc; this._queue[0].push(module); this._queue[1].push(method); this._queue[2].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 时,经过如下的流程:

betway体育app 6

简单来说,正是在调用 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通讯机制详细明白 一文中的“事件响应”风姿罗曼蒂克节。

React的音讯循环

这是第一流的事件驱动机制和新闻循环,当无其余事件时,runloop处于睡眠状态,当有事件时,比方客户操作,停车计时器届时,网络事件等等,触发意气风发此消息循环,最总表现为UI的改观或数量的更改。

betway体育app 7消息循环.png

这里要注意的是,以上我们讲到从 JS调用到Native是调用global.nativeFlushQueueImmediate 立即施行的。React音讯循环这里做了贰次缓存,举个例子客户点击叁次,全数触发的JS->N的调用都缓存到MessageQueue里,当N->JS调用完毕时,以重返值的方式重回MessageQueue, 减少了N->JS的相互次数。缓存时间是 MIN_TIME_BETWEEN_FLUSHES_MS = 5纳秒内的调用。

 __nativeCall(module, method, params, onFail, onSucc) { this._queue[MODULE_IDS].push; this._queue[METHOD_IDS].push; this._queue[PARAMS].push; var now = new Date().getTime(); if (global.nativeFlushQueueImmediate && now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) { global.nativeFlushQueueImmediate(this._queue); this._queue = [[],[],[]]; this._lastFlush = now; } }

MIN_TIME_BETWEEN_FLUSHES_MS岁月内的调用都会缓存到this._queue,以再次回到值的方式重返给Native,产生二回新闻循环

 callFunctionReturnFlushedQueue(module, method, args) { guard => { this.__callFunction(module, method, args); this.__callImmediates; return this.flushedQueue(); } flushedQueue() { this.__callImmediates(); let queue = this._queue; this._queue = [[],[],[]]; return queue[0].length ? queue : null; }

本篇的剧情就是那些,想看懂轻松,想尽量简洁明了的下结论成文字真是意气风发件十分不易于的事情,非常是这里相当多JS的代码。万分大家留言指正。下生龙活虎篇将介绍ReactNative的渲染原理。

[Native] 搜聚各类模块的 config

透过上一步成功全人体模型块的概念后,RCTBatchedBridge 中会把具有模块消息汇聚访问起来,并且依照定点的格式重新组织,最终会把那个模块新闻种类化后注入到 JavaScript 中。

主导代码如下:

// RCTBatchedBridge.m
- (NSString *)moduleConfig
{
  NSMutableArray<NSArray *> *config = [NSMutableArray new];
  for (RCTModuleData *moduleData in _moduleDataByID) {
    if (self.executorClass == [RCTJSCExecutor class]) {
      [config addObject:@[moduleData.name]];
    } else {
      [config addObject:RCTNullIfNil(moduleData.config)];
    }
  }

  return RCTJSONStringify(@{
    @"remoteModuleConfig": config,
  }, NULL);
}

@RCTModuleData
- (NSArray *)config
{
  [self gatherConstants];
  __block NSDictionary<NSString *, id> *constants = _constantsToExport;
  _constantsToExport = nil; // Not needed anymore

  if (constants.count == 0 && self.methods.count == 0) {
    return (id)kCFNull; // Nothing to export
  }

  RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData config] %@", _moduleClass], nil);

  NSMutableArray<NSString *> *methods = self.methods.count ? [NSMutableArray new] : nil;
  NSMutableArray<NSNumber *> *asyncMethods = nil;
  for (id<RCTBridgeMethod> method in self.methods) {
    if (method.functionType == RCTFunctionTypePromise) {
      if (!asyncMethods) {
        asyncMethods = [NSMutableArray new];
      }
      [asyncMethods addObject:@(methods.count)];
    }
    [methods addObject:method.JSMethodName];
  }

  NSMutableArray *config = [NSMutableArray new];
  [config addObject:self.name];
  if (constants.count) {
    [config addObject:constants];
  }
  if (methods) {
    [config addObject:methods];
    if (asyncMethods) {
      [config addObject:asyncMethods];
    }
  }
  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData config] %@", _moduleClass], nil);
  return config;
}

1.JavaScriptModule模块注册(Java层卡塔尔国

系统注册了有的常用的JS模块,在CoreModulesPackage的createJSModules中登记了一些系统宗旨的JS模块,举个例子AppRegistry(CR-VN组件注册模块卡塔尔/RCTEventEmitter(事件发射模块卡塔尔等。

class CoreModulesPackage implements ReactPackage{
  @Override
  public List<NativeModule> createNativeModules(
      ReactApplicationContext catalystApplicationContext) {
    ......
    return Arrays.<NativeModule>asList(
        new AnimationsDebugModule(
            catalystApplicationContext,
            mReactInstanceManager.getDevSupportManager().getDevSettings()),
        new AndroidInfoModule(),
        new DeviceEventManagerModule(catalystApplicationContext, mHardwareBackBtnHandler),
        new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager()),
        new Timing(catalystApplicationContext),
        new SourceCodeModule(
            mReactInstanceManager.getSourceUrl(),
            mReactInstanceManager.getDevSupportManager().getSourceMapUrl()),
        uiManagerModule,
        new DebugComponentOwnershipModule(catalystApplicationContext));
  }

  @Override
  public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Arrays.asList(
        DeviceEventManagerModule.RCTDeviceEventEmitter.class,
        JSTimersExecution.class,
        RCTEventEmitter.class,
        RCTNativeAppEventEmitter.class,
        AppRegistry.class,
        com.facebook.react.bridge.Systrace.class,
        DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);
  }
}

public interface ReactPackage {

  /**
   * @param reactContext react application context that can be used to create modules
   * @return list of native modules to register with the newly created catalyst instance
   */
  List<NativeModule> createNativeModules(ReactApplicationContext reactContext);

  /**
   * @return list of JS modules to register with the newly created catalyst instance.
   *
   * IMPORTANT: Note that only modules that needs to be accessible from the native code should be
   * listed here. Also listing a native module here doesn't imply that the JS implementation of it
   * will be automatically included in the JS bundle.
   */
  List<Class<? extends JavaScriptModule>> createJSModules();

  /**
   * @return a list of view managers that should be registered with {@link UIManagerModule}
   */
  List<ViewManager> createViewManagers(ReactApplicationContext reactContext);
}

以RCT伊夫ntEmitter这几个JavaScriptModule模块为例来看下JS模块在Java层的完结。

享有JS层组件完毕JavaScriptModule接口,例如RCT伊夫ntEmitter:

public interface RCTEventEmitter extends JavaScriptModule {
  public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
  public void receiveTouches(
      String eventName,
      WritableArray touches,
      WritableArray changedIndices);
}

能够看出来JS模块在Java层只是简短的接轨JavaScriptModule,并从未具体的得以完结,那么难题就来了,光是多个接口是万不得已调用的,好像到这里就追踪不下来了?其实可以换种思路,JS模块料定是要在Native中登记的,大家先到ReactInstanceManagerImpl中的createReactContext方法中去看下怎么注册的。

所有的NativeModule和JSModule在ReactInstanceManagerImpl中注册

办法的代码非常多,可是逻辑不复杂,关键的多少个步骤笔者都在代码中一向做了讲明,就不多说了,这里重要深入分析JS模块的挂号,所以大家看看JavaScriptModulesConfig中。

private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor,
      JSBundleLoader jsBundleLoader) {
    FLog.i(ReactConstants.TAG, "Creating react context.");
  //Native模块
    NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
  //JS模块
    JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();

  //React Context
    ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
    if (mUseDeveloperSupport) {
      reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
    }

  //处理CoreModules---包含Native模块和JS模块
    Systrace.beginSection(
        Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
        "createAndProcessCoreModulesPackage");
    try {
      CoreModulesPackage coreModulesPackage =
          new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
      processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    // 处理用户注册的模块
    for (ReactPackage reactPackage : mPackages) {
      Systrace.beginSection(
          Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
          "createAndProcessCustomReactPackage");
      try {
        processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
      } finally {
        Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
      }
    }

  //buildNativeModuleRegistry
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
    NativeModuleRegistry nativeModuleRegistry;
    try {
       nativeModuleRegistry = nativeRegistryBuilder.build();
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

  //buildJSModuleConfig
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildJSModuleConfig");
    JavaScriptModulesConfig javaScriptModulesConfig;
    try {
      javaScriptModulesConfig = jsModulesBuilder.build();
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

  ...
    //createCatalystInstance---Java/JS/C++三方通信总管
    CatalystInstanceImpl.Builder catalystInstanceBuilder = new      CatalystInstanceImpl.Builder()
        .setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
        .setJSExecutor(jsExecutor)
        .setRegistry(nativeModuleRegistry)
        .setJSModulesConfig(javaScriptModulesConfig)
        .setJSBundleLoader(jsBundleLoader)
        .setNativeModuleCallExceptionHandler(exceptionHandler);

    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
    CatalystInstance catalystInstance;
    try {
      catalystInstance = catalystInstanceBuilder.build();
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    if (mBridgeIdleDebugListener != null) {
      catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
    }

    reactContext.initializeWithInstance(catalystInstance);

  //runJSBundle
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
    try {
      catalystInstance.runJSBundle();
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    ReactMarker.logMarker("CREATE_REACT_CONTEXT_END");
    return reactContext;
  }

总的来看JavaScriptModulesConfig,首若是对每一种JavaScriptModule结构JavaScriptModuleRegistration,寄放在mModules中

/**
 * Class stores configuration of javascript modules that can be used across the bridge
 */
public class JavaScriptModulesConfig {

  private final List<JavaScriptModuleRegistration> mModules;

  private JavaScriptModulesConfig(List<JavaScriptModuleRegistration> modules) {
    mModules = modules;
  }

  /*package*/ List<JavaScriptModuleRegistration> getModuleDefinitions() {
    return mModules;
  }

  /*package*/ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
    jg.writeStartObject();
    for (JavaScriptModuleRegistration registration : mModules) {
      jg.writeObjectFieldStart(registration.getName());
      appendJSModuleToJSONObject(jg, registration);
      jg.writeEndObject();
    }
    jg.writeEndObject();
  }

  private void appendJSModuleToJSONObject(
      JsonGenerator jg,
      JavaScriptModuleRegistration registration) throws IOException {
    jg.writeObjectField("moduleID", registration.getModuleId());
    jg.writeObjectFieldStart("methods");
    for (Method method : registration.getMethods()) {
      jg.writeObjectFieldStart(method.getName());
      jg.writeObjectField("methodID", registration.getMethodId(method));
      jg.writeEndObject();
    }
    jg.writeEndObject();
  }

  public static class Builder {

    private int mLastJSModuleId = 0;
    private List<JavaScriptModuleRegistration> mModules =
        new ArrayList<JavaScriptModuleRegistration>();

    public Builder add(Class<? extends JavaScriptModule> moduleInterfaceClass) {
      int moduleId = mLastJSModuleId++;
      mModules.add(new JavaScriptModuleRegistration(moduleId, moduleInterfaceClass));
      return this;
    }

    public JavaScriptModulesConfig build() {
      return new JavaScriptModulesConfig(mModules);
    }
  }
}

/**
 * Registration info for a {@link JavaScriptModule}. 
    Maps its methods to method ids.
 */
class JavaScriptModuleRegistration {

  private final int mModuleId;
  private final Class<? extends JavaScriptModule> mModuleInterface;
  private final Map<Method, Integer> mMethodsToIds;
  private final Map<Method, String> mMethodsToTracingNames;

  JavaScriptModuleRegistration(int moduleId, Class<? extends JavaScriptModule> moduleInterface) {
    mModuleId = moduleId;
    mModuleInterface = moduleInterface;

    mMethodsToIds = MapBuilder.newHashMap();
    mMethodsToTracingNames = MapBuilder.newHashMap();
    final Method[] declaredMethods = mModuleInterface.getDeclaredMethods();
    Arrays.sort(declaredMethods, new Comparator<Method>() {
      @Override
      public int compare(Method lhs, Method rhs) {
        return lhs.getName().compareTo(rhs.getName());
      }
    });

    // Methods are sorted by name so we can dupe check and have obvious ordering
    String previousName = null;
    for (int i = 0; i < declaredMethods.length; i++) {
      Method method = declaredMethods[i];
      String name = method.getName();
      Assertions.assertCondition(
          !name.equals(previousName),
          "Method overloading is unsupported: " + mModuleInterface.getName() + "#" + name);
      previousName = name;

      mMethodsToIds.put(method, i);
      mMethodsToTracingNames.put(method, "JSCall__" + getName() + "_" + method.getName());
    }
  }
  ......
}

到此地就有了独具的JavaScriptModules,况且都围观寄放在JavaScriptModulesConfig中,那么奥迪Q5N框架是怎么利用的?再回想前边ReactInstanceManagerImpl,将JavaScriptModulesConfig用来实例化catalystInstance,这几个是三方通讯的中间转播站。

 //createCatalystInstance---Java/JS/C++三方通信总管
    CatalystInstanceImpl.Builder catalystInstanceBuilder = new  CatalystInstanceImpl.Builder()
        .setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
        .setJSExecutor(jsExecutor)
        .setRegistry(nativeModuleRegistry)
        .setJSModulesConfig(javaScriptModulesConfig)
        .setJSBundleLoader(jsBundleLoader)
        .setNativeModuleCallExceptionHandler(exceptionHandler);

Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
    CatalystInstance catalystInstance;
    try {
      catalystInstance = catalystInstanceBuilder.build();
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

catalystInstance是叁个接口,完结类是CatalystInstanceImpl,跟到布局函数中:

private CatalystInstanceImpl(
      final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry registry,
      final JavaScriptModulesConfig jsModulesConfig,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    ...
    mJSModuleRegistry = new JavaScriptModuleRegistry(CatalystInstanceImpl.this, jsModulesConfig);
    ...
  }

透过JavaScriptModulesConfig实例化JavaScriptModuleRegistry,后面深入分析过JavaScriptModule在Java层都以implements JavaScriptModule的接口,那么怎么去调用方法?这里就是对各样接口通过动态代理实例化三个代理对象,Java调用JS方法时统一分发到JavaScriptModuleInvocationHandler中的invoke进行管理,在invoke中实际上正是调用mCatalystInstance.callFunction

很明朗,接下去需求到JavaScriptModuleRegistry中看看:

/**
 * Class responsible for holding all the {@link JavaScriptModule}s registered to this
 * {@link CatalystInstance}. 
 Uses Java proxy objects to dispatch method calls on JavaScriptModules
 * to the bridge using the corresponding module and method ids so the proper function is executed in
 * JavaScript.
 */
/*package*/ class JavaScriptModuleRegistry {

  private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances;

  public JavaScriptModuleRegistry(
      CatalystInstanceImpl instance,
      JavaScriptModulesConfig config) {
    mModuleInstances = new HashMap<>();
    for (JavaScriptModuleRegistration registration : config.getModuleDefinitions()) {
      Class<? extends JavaScriptModule> moduleInterface = registration.getModuleInterface();
      JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
          moduleInterface.getClassLoader(),
          new Class[]{moduleInterface},
          new JavaScriptModuleInvocationHandler(instance, registration));

      mModuleInstances.put(moduleInterface, interfaceProxy);
    }
  }

  public <T extends JavaScriptModule> T getJavaScriptModule(Class<T> moduleInterface) {
    return (T) Assertions.assertNotNull(
        mModuleInstances.get(moduleInterface),
        "JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
  }

  private static class JavaScriptModuleInvocationHandler implements InvocationHandler {

    private final CatalystInstanceImpl mCatalystInstance;
    private final JavaScriptModuleRegistration mModuleRegistration;

    public JavaScriptModuleInvocationHandler(
        CatalystInstanceImpl catalystInstance,
        JavaScriptModuleRegistration moduleRegistration) {
      mCatalystInstance = catalystInstance;
      mModuleRegistration = moduleRegistration;
    }

    @Override
    public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      String tracingName = mModuleRegistration.getTracingName(method);
      mCatalystInstance.callFunction(
          mModuleRegistration.getModuleId(),
          mModuleRegistration.getMethodId(method),
          Arguments.fromJavaArgs(args),
          tracingName);
      return null;
    }
  }
}

在Java调用JS模块的秘诀时,都会经过代办对象调用invoke方法,在invoke方法中调用CatalystInstance.callFunction,CatalystInstance是三个接口,达成类CatalystInstanceImpl,看见CatalystInstanceImpl中的callFunction:

/* package */ void callFunction(
      final int moduleId,
      final int methodId,
      final NativeArray arguments,
      final String tracingName) {
    if (mDestroyed) {
      FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
      return;
    }

    incrementPendingJSCalls();

    final int traceID = mTraceID++;
    Systrace.startAsyncFlow(
        Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
        tracingName,
        traceID);

    mCatalystQueueConfiguration.getJSQueueThread().runOnQueue(
        new Runnable() {
          @Override
          public void run() {
            mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();

            Systrace.endAsyncFlow(
                Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
                tracingName,
                traceID);

            if (mDestroyed) {
              return;
            }

            Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, tracingName);
            try {
              Assertions.assertNotNull(mBridge).callFunction(moduleId, methodId, arguments);
            } finally {
              Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
            }
          }
        });
  }

所有Java层向Javascript层的通信央浼都以走的ReactBridge.callFunction

实在正是在JSQueueThread线程中调用ReactBridge的callFunction,是Native函数,到此处就从Java转到C++层了

/**
 * Interface to the JS execution environment and means of transport for messages Java<->JS.
 */
@DoNotStrip
public class ReactBridge extends Countable {

  /* package */ static final String REACT_NATIVE_LIB = "reactnativejni";

  static {
    SoLoader.loadLibrary(REACT_NATIVE_LIB);
  }

  private final ReactCallback mCallback;
  private final JavaScriptExecutor mJSExecutor;
  private final MessageQueueThread mNativeModulesQueueThread;

  /**
   * @param jsExecutor the JS executor to use to run JS
   * @param callback the callback class used to invoke native modules
   * @param nativeModulesQueueThread the MessageQueueThread the callbacks should be invoked on
   */
  public ReactBridge(
      JavaScriptExecutor jsExecutor,
      ReactCallback callback,
      MessageQueueThread nativeModulesQueueThread) {
    mJSExecutor = jsExecutor;
    mCallback = callback;
    mNativeModulesQueueThread = nativeModulesQueueThread;
    initialize(jsExecutor, callback, mNativeModulesQueueThread);
  }

  @Override
  public void dispose() {
    mJSExecutor.close();
    mJSExecutor.dispose();
    super.dispose();
  }

  public void handleMemoryPressure(MemoryPressure level) {
    switch (level) {
      case MODERATE:
        handleMemoryPressureModerate();
        break;
      case CRITICAL:
        handleMemoryPressureCritical();
        break;
      default:
        throw new IllegalArgumentException("Unknown level: " + level);
    }
  }

  private native void initialize(
      JavaScriptExecutor jsExecutor,
      ReactCallback callback,
      MessageQueueThread nativeModulesQueueThread);

  /**
   * All native functions are not thread safe and appropriate queues should be used
   */
  public native void loadScriptFromAssets(AssetManager assetManager, String assetName);
  public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL);
  public native void callFunction(int moduleId, int methodId, NativeArray arguments);
  public native void invokeCallback(int callbackID, NativeArray arguments);
  public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);
  public native boolean supportsProfiling();
  public native void startProfiler(String title);
  public native void stopProfiler(String title, String filename);
  private native void handleMemoryPressureModerate();
  private native void handleMemoryPressureCritical();
}

再回头看看ReactBridge在CatalystInstanceImpl里面包车型地铁开始化,早先化会ReactBridge调用setGlobalVariable,那是个Native函数,是在C++层注册用的,先按下前面再解析组件调用进度再剖判,先看看 buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig卡塔尔(英语:State of Qatar)做了何等。

private CatalystInstanceImpl(
      final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
      final JavaScriptExecutor jsExecutor,
      final NativeModuleRegistry registry,
      final JavaScriptModulesConfig jsModulesConfig,
      final JSBundleLoader jsBundleLoader,
      NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    ......
    try {
      mBridge = mCatalystQueueConfiguration.getJSQueueThread().callOnQueue(
          new Callable<ReactBridge>() {
            @Override
            public ReactBridge call() throws Exception {
              Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
              try {
                return initializeBridge(jsExecutor, jsModulesConfig);
              } finally {
                Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
              }
            }
          }).get(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS);
    } catch (Exception t) {
      throw new RuntimeException("Failed to initialize bridge", t);
    }
  }

  private ReactBridge initializeBridge(
      JavaScriptExecutor jsExecutor,
      JavaScriptModulesConfig jsModulesConfig) {
    mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
    Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");

    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
    ReactBridge bridge;
    try {
      bridge = new ReactBridge(
          jsExecutor,
          new NativeModulesReactCallback(),
          mCatalystQueueConfiguration.getNativeModulesQueueThread());
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
    try {
      bridge.setGlobalVariable(
          "__fbBatchedBridgeConfig",
          buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
      bridge.setGlobalVariable(
          "__RCTProfileIsProfiling",
          Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
    } finally {
      Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
    }

    return bridge;
  }

在CatalystInstanceImpl中的完毕,其实正是写一个JSON字符串,有五个字段"remoteModuleConfig"和"localModulesConfig",分别对应NativeModule(Native提供给JS调用卡塔尔(قطر‎和JSModule(JS提供给Native调用卡塔尔(قطر‎

private String buildModulesConfigJSONProperty(
      NativeModuleRegistry nativeModuleRegistry,
      JavaScriptModulesConfig jsModulesConfig) {
    JsonFactory jsonFactory = new JsonFactory();
    StringWriter writer = new StringWriter();
    try {
      JsonGenerator jg = jsonFactory.createGenerator(writer);
      jg.writeStartObject();
      jg.writeFieldName("remoteModuleConfig");
      nativeModuleRegistry.writeModuleDescriptions(jg);
      jg.writeFieldName("localModulesConfig");
      jsModulesConfig.writeModuleDescriptions(jg);
      jg.writeEndObject();
      jg.close();
    } catch (IOException ioe) {
      throw new RuntimeException("Unable to serialize JavaScript module declaration", ioe);
    }
    return writer.getBuffer().toString();
  }

再看看localModulesConfig都写入了怎么着,跟进去JavaScriptModulesConfig.java,正是往JSON字符创中先写入接口名、moduleID、methods(方法名、methodID卡塔尔(英语:State of Qatar)等。

/*package*/ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
    jg.writeStartObject();
    for (JavaScriptModuleRegistration registration : mModules) {
      jg.writeObjectFieldStart(registration.getName());
      appendJSModuleToJSONObject(jg, registration);
      jg.writeEndObject();
    }
    jg.writeEndObject();
  }

  private void appendJSModuleToJSONObject(
      JsonGenerator jg,
      JavaScriptModuleRegistration registration) throws IOException {
    jg.writeObjectField("moduleID", registration.getModuleId());
    jg.writeObjectFieldStart("methods");
    for (Method method : registration.getMethods()) {
      jg.writeObjectFieldStart(method.getName());
      jg.writeObjectField("methodID", registration.getMethodId(method));
      jg.writeEndObject();
    }
    jg.writeEndObject();
  }

initializeBridge->setGlobalVariable->buildModulesConfigJSONProperty->writeModuleDescriptions

整整经过功效是将有着JavaScriptModule的音信更改JSON字符串预先保存到Bridge中, 通过methodID标记作为key存入JSON生成器中,用来最后生成JSON字符串:

中间setGlobalVariable也是Native函数,buildModulesConfigJSONProperty把具备的JavaScriptModule模块以moduleID+methodID花样转换JSON字符串,通过setGlobalVariable把JSON字符串预先存入了ReactBridge

 bridge.setGlobalVariable(
          "__fbBatchedBridgeConfig",
          buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));

Java层的调用便是上边的进度,最终经过JNI调用到C++层,接下去步入那风度翩翩层看看。

六、有待商量的从头到尾的经过

setupExecutor

此地设置的是 JS 引擎,相仿分为调节和测量试验情状和生育条件:
在调度意况下,对应的 Executor 为 RCTWebSocketExecutor,它经过 WebSocket 连采纳 Chrome 中,在 Chrome 里运营 JS;
在生养条件下,对应的 Executor 为 RCTContextExecutor,那应该就是传说中的 javascriptcore

Native调用JS (Native->JS)

能够看见,运维上马之后,Native调用了JS的 RCTDevice伊夫ntEmitter.emit 广播了三个事件 appStateDidChange,networkStatusDidChange随后调用 AppRegistry.runApplication(["MGReactNative",{"rootTag":1,"initialProps":{}}]卡塔尔国运行了React Native引擎。下边大家一小点深入分析,是假若从Native调用到JS的函数AppRegistry.runApplication的

JSContext *context = [[JSContext alloc] init];[context evaluateScript:@"function add { return a + b; }"];JSValue *add = context[@"add"];NSLog(@"Func: %@", add); JSValue *sum = [add callWithArguments:@[@]];NSLog(@"Sum: %d",[sum toInt32]);//OutPut:// Func: function add { return a + b; }// Sum: 28

JSContext 是运维 JavaScript 代码的条件。四个 JSContext 是叁个大局境况的实例,大家能够从 JSContext全局变量中用下标的不二等秘书技抽取JS代码中定义的函数 add,它用JSValue类型包装了一个 JS 函数, 假若你规定JSValue是一个JS函数类型,可以采用callWithArguments 来调用它。更详尽的介绍能够学学那篇小说 Java​Script​Core

聪慧的你一定想到,React Native 的也是用平等方式调用到AppRegistry.runApplication,是的,不过是通过三个通用接口来调用的RCTJavaScriptContext 封装了OC方法callFunction,

- callFunctionOnModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args callback:(RCTJavaScriptCallback)onComplete{ // TODO: Make this function handle first class instead of dynamically dispatching it. #9317773 [self _executeJSCall:@"callFunctionReturnFlushedQueue" arguments:@[module, method, args] callback:onComplete];}

_executeJSCall 试行的具体代码是

 method = @“callFunctionReturnFlushedQueue” JSStringRef moduleNameJSStringRef = JSStringCreateWithUTF8CString("__fbBatchedBridge"); JSValueRef moduleJSRef = JSObjectGetProperty(contextJSRef, globalObjectJSRef, moduleNameJSStringRef, &errorJSRef); JSStringRef methodNameJSStringRef = JSStringCreateWithCFString((__bridge CFStringRef)method); JSValueRef methodJSRef = JSObjectGetProperty(contextJSRef, (JSObjectRef)moduleJSRef, methodNameJSStringRef, resultJSRef = JSObjectCallAsFunction(contextJSRef, (JSObjectRef)methodJSRef, (JSObjectRef)moduleJSRef, 0, NULL, 

能够看出Native 从JSContext中拿出JS全局对象 __fbBatchedBridge,然后调用了其callFunctionReturnFlushedQueue函数

时序图总览

先来看下全体的时序图,后边会挨个介绍。

betway体育app 8

image

React Native中调用全体的行为都以从Native端发起的,顾客操作直接面向的也是Native。所以这一个通讯模型又有啥不可视作是Native号令对话,然后Javascript实行回答。

二、开头化流程

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 的虚构境况去履行有关代码,效果相近。

GitHub MGReactNativeTest工程里有直接退换main.jsbundle

[JS] UIManager 的再加工

native 生成 __fbBatchedBridgeConfig 配置新闻的时候,view 模块和非 view 模块(区别是 view 模块都以由 RC电视iewManager 来保管)是各自对待的,全体的 view 模块消息都位居了 "RCTUIManager" 的质量对象中,参照 __fbBatchedBridgeConfig的布局大要上是上边那样的。

通过 NativeModules 的变迁进程一气浑成了 remoteModuleConfig 中外层模块的管理(包涵 NativeModules.UIManager 也在这里大器晚成经过中变化了启幕版本),那 UIManager 的再加工过程则是解析和处理RCTUIManager 内种种 view 的进程。

{
    "remoteModuleConfig": [
        [
            // 模块名称
            "RCTUIManager",
            // 属性对象
            {
                // 此处省略
                "RCTRawText": {
                    "Manager": "RCTRawTextManager",
                    "NativeProps": {
                        "text": "NSString"
                    }
                },
                "RCTSwitch": {
                    "Manager": "RCTSwitchManager",
                    "NativeProps": {
                        "thumbTintColor": "UIColor",
                        "tintColor": "UIColor",
                        "value": "BOOL",
                        "onChange": "BOOL",
                        "onTintColor": "UIColor",
                        "disabled": "BOOL"
                    }
                }
                // 此处省略
            },
            // 方法列表
            [
                "removeSubviewsFromContainerWithID",
                "removeRootView",
                "replaceExistingNonRootView",
                "setChildren",
                "manageChildren",
                "createView",
                "updateView",
                "focus",
                "blur",
                "findSubviewIn",
                "dispatchViewManagerCommand",
                "measure",
                "measureInWindow",
                "measureLayout",
                "measureLayoutRelativeToParent",
                "measureViewsInRect",
                "takeSnapshot",
                "setJSResponder",
                "clearJSResponder",
                "configureNextLayoutAnimation"
            ],
            // Promise 方法 index(方法列表中哪些方法是异步的)
            [
                16
            ]
        ],

UIManager 的再加工最重要包括八个地点,为 UIManager 上的每一个 view 增添Constants 和 Commands 属性。

  • 1 生成 Constants 属性

    • 获取每种 view 的 Manager 名称,举个例子 RCTSwitchManager
    • 在 NativeModules 上查找 RCTSwitchManager 对象
    • 假定期存款在则将 RCTSwitchManager 对象中兼有非 function 属性放入Constants
// UIManager.js
const viewConfig = UIManager[viewName];
if (viewConfig.Manager) {
let constants;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Constants', {
  configurable: true,
  enumerable: true,
  get: () => {
    if (constants) {
      return constants;
    }
    constants = {};
    const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
    viewManager && Object.keys(viewManager).forEach(key => {
      const value = viewManager[key];
      if (typeof value !== 'function') {
        constants[key] = value;
      }
    });
    return constants;
  },
});   
  • 2 生成 Commands 属性

    • 得到每个 view 的 Manager 名称,举例 RCTSwitchManager
    • 在 NativeModules 上查找 RCTSwitchManager 对象
    • 风姿洒脱经存在则将 RCTSwitchManager 对象中持有 function 属性放入Commands,value 为属性的索引值
// UIManager.js
let commands;
/* $FlowFixMe - nice try. Flow doesn't like getters */
Object.defineProperty(viewConfig, 'Commands', {
  configurable: true,
  enumerable: true,
  get: () => {
    if (commands) {
      return commands;
    }
    commands = {};
    const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
    let index = 0;
    viewManager && Object.keys(viewManager).forEach(key => {
      const value = viewManager[key];
      if (typeof value === 'function') {
        commands[key] = index++;
      }
    });
    return commands;
  },
});

3.JavaScript层实现

Javascript的通讯,实质上是Weikit执行Javascript说话,调用流程是Bridge->WebKit->JavascriptWebKit中提供了大多与Javascript通信的API,比如evaluateScriptJSContextGetGlobalObjectJSObjectSetProperty等。

遥想一下如今JSCExecutor::setGlobalVariable:

//jni/react/JSCExecutor.cpp
void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
  auto globalObject = JSContextGetGlobalObject(m_context);
  String jsPropertyName(propName.c_str());

  String jsValueJSON(jsonValue.c_str());
  auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);

  JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
}

所有JavaScriptModule音讯是调用的setGlobalVariable主意生成一张映射表,那张映射表最后必然是要保存在Javascript层的,JSContextGetGlobalObjectWeiKit的章程,其目标是收获Global大局对象,jsPropertyName措施字面意思就是Javascript目的的属性名,参数propName是从Java层传递过来的,在CatalystInstanceImpl.java类中能够证实这点,具体值有五个:__fbBatchedBridgeConfig__RCTProfileIsProfiling.

//com/facebook/react/bridge/CatalystInstanceImpl
bridge.setGlobalVariable(
          "__fbBatchedBridgeConfig",
          buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));

翻译成JS语句大约是:

global.__fbBatchedBridgeConfig = jsonValue;

由于__fbBatchedBridgeConfig目的是被一贯定义成Global全局对象的质量,就足以一贯调用了,相近于window对象,__fbBatchedBridgeConfig目的里又有七个性情:remoteModuleConfiglocalModulesConfig,分别对应Java模块和JS模块。

到JS层的BatchedBridge.js:

//Libraries/BatchedBridge/BatchedBridge.js
const MessageQueue = require('MessageQueue');

const BatchedBridge = new MessageQueue(
  __fbBatchedBridgeConfig.remoteModuleConfig,   //Native(Java)模块
  __fbBatchedBridgeConfig.localModulesConfig,   //JS模块
);

// TODO: Move these around to solve the cycle in a cleaner way.

const Systrace = require('Systrace');
const JSTimersExecution = require('JSTimersExecution');

BatchedBridge.registerCallableModule('Systrace', Systrace);
BatchedBridge.registerCallableModule('JSTimersExecution', JSTimersExecution);

if (__DEV__) {
  BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));
}

Object.defineProperty(global, '__fbBatchedBridge', { value: BatchedBridge });

module.exports = BatchedBridge;

1.在全局变量global中定义fbBatchedBridge,在前段时间JSCExecutor::callFunction中调用JS语句的方式是fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})

2.BatchedBridge=new MessageQueue

据此再持续跟到MessageQueue.js中:

class MessageQueue {

  constructor(remoteModules, localModules) {
    this.RemoteModules = {};

    this._callableModules = {};
    this._queue = [[], [], [], 0];
    this._moduleTable = {};
    this._methodTable = {};
    this._callbacks = [];
    this._callbackID = 0;
    this._callID = 0;
    this._lastFlush = 0;
    this._eventLoopStartTime = new Date().getTime();

    [
      'invokeCallbackAndReturnFlushedQueue',
      'callFunctionReturnFlushedQueue',
      'flushedQueue',
    ].forEach((fn) => this[fn] = this[fn].bind(this));

    let modulesConfig = this._genModulesConfig(remoteModules);
    this._genModules(modulesConfig);
    localModules && this._genLookupTables(
      this._genModulesConfig(localModules),this._moduleTable, this._methodTable
    );

    this._debugInfo = {};
    this._remoteModuleTable = {};
    this._remoteMethodTable = {};
    this._genLookupTables(
      modulesConfig, this._remoteModuleTable, this._remoteMethodTable
    );
  }

  /**
   * Public APIs
   */
  callFunctionReturnFlushedQueue(module, method, args) {
    guard(() => {
      this.__callFunction(module, method, args);
      this.__callImmediates();
    });

    return this.flushedQueue();
  }

  _genLookupTables(modulesConfig, moduleTable, methodTable) {
    modulesConfig.forEach((config, moduleID) => {
      this._genLookup(config, moduleID, moduleTable, methodTable);
    });
  }

  _genLookup(config, moduleID, moduleTable, methodTable) {
    if (!config) {
      return;
    }

    let moduleName, methods;
    if (moduleHasConstants(config)) {
      [moduleName, , methods] = config;
    } else {
      [moduleName, methods] = config;
    }

    moduleTable[moduleID] = moduleName;
    methodTable[moduleID] = Object.assign({}, methods);
  }
}

module.exports = MessageQueue;

把remoteModules, localModules传进了_genLookupTables的不二秘诀里,同偶尔间还会有八个参数_moduleTable_methodTable,就是大家找的映射表了,一张module映射表,一张method映射表

再回首下前边Java调用JS的语句格局:__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})

class MessageQueue {

 constructor(remoteModules, localModules) {
   this.RemoteModules = {};

   this._callableModules = {};
   this._queue = [[], [], [], 0];
   this._moduleTable = {};
   this._methodTable = {};
   this._callbacks = [];
   this._callbackID = 0;
   this._callID = 0;
   this._lastFlush = 0;
   this._eventLoopStartTime = new Date().getTime();

   [
     'invokeCallbackAndReturnFlushedQueue',
     'callFunctionReturnFlushedQueue',
     'flushedQueue',
   ].forEach((fn) => this[fn] = this[fn].bind(this));

   let modulesConfig = this._genModulesConfig(remoteModules);
   this._genModules(modulesConfig);
   localModules && this._genLookupTables(
     this._genModulesConfig(localModules),this._moduleTable, this._methodTable
   );

   this._debugInfo = {};
   this._remoteModuleTable = {};
   this._remoteMethodTable = {};
   this._genLookupTables(
     modulesConfig, this._remoteModuleTable, this._remoteMethodTable
   );
 }

 /**
  * Public APIs
  */
 callFunctionReturnFlushedQueue(module, method, args) {
   guard(() => {
     this.__callFunction(module, method, args);
     this.__callImmediates();
   });

   return this.flushedQueue();
 }

 _genLookupTables(modulesConfig, moduleTable, methodTable) {
   modulesConfig.forEach((config, moduleID) => {
     this._genLookup(config, moduleID, moduleTable, methodTable);
   });
 }

 _genLookup(config, moduleID, moduleTable, methodTable) {
   if (!config) {
     return;
   }

   let moduleName, methods;
   if (moduleHasConstants(config)) {
     [moduleName, , methods] = config;
   } else {
     [moduleName, methods] = config;
   }

   moduleTable[moduleID] = moduleName;
   methodTable[moduleID] = Object.assign({}, methods);
 }
}

module.exports = MessageQueue;

在那之中有个callableModules, 它正是用来寄放在哪些Javascript组件是足以被调用的,正常意况下**callableModules的数据和JavaScriptModules**的数码(满含方法名和参数)理应是一心对应的.

看下哪些JS组件调用registerCallableModule实市场价格势的登记

betway体育app 9

resigterCallableModule.png

看一下RCTEventEmitter.js:

//Libraries/BatchedBridge/BatchedBridgedModules/RCTEventEmitter.js
var BatchedBridge = require('BatchedBridge');
var ReactNativeEventEmitter = require('ReactNativeEventEmitter');

BatchedBridge.registerCallableModule(
  'RCTEventEmitter',
  ReactNativeEventEmitter
);

// Completely locally implemented - no native hooks.
module.exports = ReactNativeEventEmitter;

ReactNativeEventEmitter.js:

var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
  receiveEvent: function(
    tag: number,
    topLevelType: string,
    nativeEventParam: Object
  ) {
    ......
  },

  receiveTouches: function(
    eventTopLevelType: string,
    touches: Array<Object>,
    changedIndices: Array<number>
    ) {
      ......
    });

比较之下下Java层的接口表示,方法名称和参数个数是雷同的

public interface RCTEventEmitter extends JavaScriptModule {
  public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
  public void receiveTouches(
      String eventName,
      WritableArray touches,
      WritableArray changedIndices);
}

继承前边__callFunction的最终一步:moduleMethods[method].apply(moduleMethods, args卡塔尔(قطر‎;调用对应模块的不二等秘书籍。

moduleClass:_moduleClass];

moduleConfig

据他们说保存的模块消息,组装成一个 JSON ,对应的字段为 remoteModuleConfig。

希图知识,对JS很熟的校友能够略过或指正

这段JS代码怎么精通吧,那一个是nodejs的模块代码,当打包成main.js之后,含义又有浮动,大家大致能够这么掌握,__d(卡塔尔(قطر‎是一个定义module的JS函数,其就十分上边这段代码中的 define 函数,从代码上超级轻便可知,它定义贰个module,名字Id为BatchedBridge,相同的时候传递了三个工厂函数,另一个模块的代码能够因此调用require获取那一个module,举例var BatchedBridge=require('BatchedBridge');

那是八个懒加运载飞机制,当有人调用require时,工厂函数才施行,在代码最终,把这一个模块要导出的开始和结果赋值给module.exports。

 function define(id,factory){ modules[id]={ factory:factory, module:{exports:{}}, isInitialized:false, hasError:false};} function require{ var mod=modules[id]; if(mod&&mod.isInitialized){ return mod.module.exports;}

好,大家赶紧回来,在上段代码中当BatchedBridge module创建时,通过那句 Object.defineProperty(global,'__fbBatchedBridge',{value:BatchedBridge}卡塔尔国; 把温馨定义到JSContext的全局变量上。所以在Native代码中能够通过 JSContext[@"__fbBatchedBridge"]获取到,

从代码中也足以见见BatchedBridge 是JS类MessageQueue的实例,并且它导出的时候并未导出构造函数MessageQueue,而是导出的实例BatchedBridge,所以它是React Native JS引擎中全局唯风流倜傥的。它也是Natvie和JS互通的主要桥梁。

 __fbBatchedBridge.callFunctionReturnFlushedQueue("AppRegistr","runApplication",["MGReactNative",{"rootTag":1,"initialProps":{}}])

大家继续看MessageQueue 类的callFunctionReturnFlushedQueue 函数,它谈起底调用到__callFunction(module, method, args)函数

__callFunction(module, method, args) { var moduleMethods = this._callableModules[module]; if (!moduleMethods) { moduleMethods = require; } moduleMethods[method].apply(moduleMethods, args); }

看起来__callFunction正是最终的散发函数了,首先它从this._callableModules中找到模块对象,假使它还尚未加载,就动态加载它,假使找到就施行最终的JS函数。

先看下AppRegistry是如何揭露给Natvie的

__d('AppRegistry',function(global, require, module, exports) { 'use strict'; var BatchedBridge=require('BatchedBridge'); var ReactNative=require('ReactNative'); var AppRegistry={ runApplication:function(appKey,appParameters){ runnables[appKey].run(appParameters); }, } BatchedBridge.registerCallableModule( 'AppRegistry', AppRegistry); module.exports=AppRegistry;});

有前方的讲明,以往看这几个相应不态费事了,能够见到AppRegistry模块工厂函数中,执行了 BatchedBridge.registerCallableModule('AppRegistry',AppRegistry卡塔尔国;,把温馨注册到BatchedBridge的CallableModule中,所以在上风流倜傥节中,__callFunction才能在_callableModules找到AppRegistry实例,手艺调用其runApplication函数。本身写的模块代码可以用React Native这种情势揭破给Natvie调用,和一直揭示的区别是,符合React Natvie的模块化原则,别的三个直观的利润是您的模块可以是懒加载的,何况不会传染全局空间。

日前终于把从N-JS的万事路线跑通了,大家梳理下整个流程看看。

1 [RCTBatchedBridge enqueueJSCall:@“AppRegistry.runApplication” args:["MGReactNative",{"rootTag":1,"initialProps":{}}]];2 RCTJavaScriptContext callFunctionOnModule:@"AppRegistr" method:@"runApplication" arguments:["MGReactNative",{"rootTag":1,"initialProps":{}}] callback:(RCTJavaScriptCallback)onComplete//main.js3 __fbBatchedBridge.callFunctionReturnFlushedQueue("AppRegistr","runApplication",["MGReactNative",{"rootTag":1,"initialProps":{}}])//main.js4 BatchedBridge.__callFunction("AppRegistr","runApplication",["MGReactNative",{"rootTag":1,"initialProps":{}}])//main.js5 var moduleMethods = BatchedBridge._callableModules[module]; if (!moduleMethods) { moduleMethods = require; } moduleMethods[method].apply(moduleMethods, args);

接下去大家看看从JS如何调用Native,换句话说Native怎样开放API给JS

大家以弹Alert框的接口为例,那是Native的OC代码,导出RCTAlertManager类的alertWithArgs:(NSDictionary *)argscallback:(RCTResponseSenderBlock)callback)方法

@interface RCTAlertManager() : NSObject <RCTBridgeModule, RCTInvalidating>...@end@implementation RCTAlertManagerRCT_EXPORT_MODULE()RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback){...}#end
  • OC类实现RCTBridgeModule协议
  • 在.m的类实现中出席RCT_EXPORT_MODULE(卡塔尔,扶助你达成RCTBridgeModule契约
  • 要导出的函数用RCT_EXPORT_METHOD()宏括起来,不用那几个宏,不会导出任何函数

未来从JS里能够这么调用那个法子:

var RCTAlertManager=require('react-native').NativeModules.AlertManager;RCTAlertManager.alertWithArgs({message:'JS->Native Call',buttons:[{k1:'button1'},{k2:'button1'}]},function {console.log('RCTAlertManager.alertWithArgs() id:' + id +' v:' + v)});

试行之后的职能,弹出八个Alert

betway体育app 10alert.png

对于详细的怎样导出函数推荐阅读Native Modules

作者们前几日的指标不是和美人喝茶聊天,是深切美丽的女人内心,是内心咳咳。来走访昨天的重大

在JS中得以一向使用RCTAlertManager.alertWithArgs来调用,表达JS中意气风发度定义了和OC对象相对应的JS对象,大家从导出多少个Native类最初,完整追踪下这些进程。

RCTAlertManager类实现了RCTBridgeModule磋商,并且在类的兑现里包蕴了RCT_EXPORT_MODULE() 宏

@protocol RCTBridgeModule <NSObject> #define RCT_EXPORT_MODULE RCT_EXTERN void RCTRegisterModule; + (NSString *)moduleName { return @#js_name; } + load { RCTRegisterModule; }// Implemented by RCT_EXPORT_MODULE+ (NSString *)moduleName;@optional

在OC里,二个类所在文书被引述时,系统会调用其+load函数,当RCTAlertManager所在文书被引述时,系统调用load 函数,函数里大致的调用RCTRegisterModule 把温馨注册到二个大局数组RCTModuleClasses,那样系统中程导弹出的类都会活动注册到那一个全局变量数组里。

在JS中有一个BatchedBridge用来和Native通信,在Natvie中也是有多个RCTBatchedBridge类,它包裹了JSContext即JS引擎在RCTBatchedBridge start 函数中,做了5件事

  1. jsbundle文件的下载或地面读取
  2. 开始化导出给JS用的Native模块
  3. 初始化JS引擎
  4. 更改配置表,并流入到JS引擎中,
  5. 执行jsbundle文件。
 //伪代码 - start{ //1 jsbundle文件的下载或本地读取 NSData *sourceCode; [self loadSource:^(NSError *error, NSData *source) {sourceCode = source}]; //2 初始化导出给JS用的Native模块 [self initModules]; //3 初始化JS引擎 [self setUpExecutor]; //4 生成Native模块配置表 把配置表注入到JS引擎中 NSSting* config = [self moduleConfig]; [self injectJSONConfiguration:config onComplete:^(NSError *error) {}); //5 最后执行jsbundle [self executeSourceCode:sourceCode];}

近年来大家最关切第二步最初化Native模块 initModules 和moduleConfig 到底是怎样

//伪代码- initModules{ //遍历上节讲到的RCTGetModuleClasses全局数组,用导出模块的类或者实例创建RCTModuleData for (Class moduleClass in RCTGetModuleClasses { NSString *moduleName = RCTBridgeModuleNameForClass(moduleClass); //这里一个很有意思的地方,如果导出的类或其任何父类重写了init方法,或者类中有setBridge方法 //则React Native假设开发者期望这个导出模块在Bridge第一次初始化时实例化,否则怎么样,大家想想 if ([moduleClass instanceMethodForSelector:@selector] != objectInitMethod || [moduleClass instancesRespondToSelector:setBridgeSelector]) { module = [moduleClass new]; } // 创建RCTModuleData RCTModuleData *moduleData; if  { moduleData = [[RCTModuleData alloc] initWithModuleInstance:module]; } else { moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self]; } //保存到数组中,数组index就是这个模块的索引 [_moduleDataByID addObject:moduleData]; }}

initModules里依据是还是不是重写init或加多了setBridge来决定是或不是要马上实例化RCTGetModuleClasses里的导出类,然后用实例或类成立RCTModuleData,缓存到地头,以便JS调用时查询。

再来看第四步导出的 NSSting* config = [self moduleConfig] 是如何内容

 {"remoteModuleConfig": [["RCTStatusBarManager"], ["RCTSourceCode"], ["RCTAlertManager"], ["RCTExceptionsManager"], ["RCTDevMenu"], ["RCTKeyboardObserver"], ["RCTAsyncLocalStorage"], . . . ]} 

它独自是三个类名数组。

生产构造表后,通过上面包车型地铁艺术把那些类名数组注入到JSContext,赋值给JS全局变量__fbBatchedBridgeConfig

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

在JS端,当有人援用了BatchedBridge var BatchedBridge=require('BatchedBridge');,其工厂函数会通过 __fbBatchedBridgeConfig配置表创造MessageQueue的实例BatchedBridge

 var MessageQueue=require('MessageQueue'); var BatchedBridge=new MessageQueue( __fbBatchedBridgeConfig.remoteModuleConfig, __fbBatchedBridgeConfig.localModulesConfig);

咱俩看看MessageQueue的结构函数,布局函数里为各种导出类创立了贰个相应的module对象,因为那时config里唯有二个导出类的名字,所以这里只为这么些指标扩充了一个分子变量 module.moduleID,并把module保存到this.RemoteModules数组里

 _genModule(config, moduleID) { let module = {}; if (!constants && !methods && !asyncMethods) { module.moduleID = moduleID; } this.RemoteModules[moduleName] = module; }

随后大家顺藤摸瓜看看这里使用的BatchedBridge.RemoteModules

NativeModules在初步化时,用BatchedBridge.RemoteModules保存的类名列表,为每一个JS对象扩张了函数等属性

__d('NativeModules',function(global, require, module, exports) { 'use strict'; var RemoteModules=require('BatchedBridge').RemoteModules; var NativeModules={}; //遍历NativeModules中导出类名 Object.keys(RemoteModules).forEach(function(moduleName){ //把类名定义为NativeModules的一个属性,比如AlertManager类,定义只有就可以用NativeModules.AlertManager 访问 Object.defineProperty(NativeModules,moduleName,{ //这个属性(AlertManager)是可以遍历的,当然属性也是个对象里面有属性和函数 enumerable:true, //属性都有get和set函数,当调用访问这个属性时,会调用get函数 NativeModules.AlertManager get:function(){ var module=RemoteModules[moduleName]; if(module&&typeof module.moduleID==='number'&&global.nativeRequireModuleConfig){ //调用Native提供的全局函数nativeRequireModuleConfig查询AlertManager 导出的常量和函数 var json=global.nativeRequireModuleConfig(moduleName); module=config&&BatchedBridge.processModuleConfig(JSON.parse,module.moduleID); RemoteModules[moduleName]=module; } return module; } }); }); module.exports=NativeModules;});

React Native 把全部的Native导出类定义在二个NativeModules模块里,所以利用Natvie接口时也能够一向那样得到相应的JS对象var RCTAlertManager=require('NativeModules').AlertManager;

代码里小编加精通说构思三个主题素材,为啥React Natvie搞的那么劳苦,为啥不在上三个步骤里(MessageQueue的结构函数)里就创立出生龙活虎体化的JS对象。

无可争辩,正是模块的懒加载,即使Native导出了Alert接口,在JS引擎最初化后,JS里只存在三个名称叫AlertManager的空对象

betway体育app 11906C7A90-0A85-4FD6-B433-39CE041D4445.png

当调用了RCTAlertManager.alertWithArgs({message:'JS->Native Call',buttons:[{k1:'button1'}时,才会调用AlertManager 的get函数到Native里询问导出的常量和函数,并定义到AlertManager中。

betway体育app 127RGT1@Z}N19_9{KQ~P_SDFE.jpg

RCTAlertManager.alertWithArgs 这些函数是什么调用到Native里的吗,在BatchedBridge.processModuleConfig函数中,用_genMethod创立了一个闭包fn为每一种函数赋值,这几个函数转调self.__nativeCall(module, method, args, onFail, onSucc卡塔尔(قطر‎; 大家调用RCTAlertManager.alertWithArgs函数,其实都以调用的这一个fn闭包。

 _genMethod(module, method, type) { fn = function { return self.__nativeCall(module, method, args, onFail, onSucc); }; return fn; }

__nativeCall,好熟识的名字,

 __nativeCall(module, method, params, onFail, onSucc) { this._queue[MODULE_IDS].push; this._queue[METHOD_IDS].push; this._queue[PARAMS].push; global.nativeFlushQueueImmediate(this._queue); this._queue = [[],[],[]]; this._lastFlush = now; }

global.nativeFlushQueueImmediate 是Native提供的接口,__nativeCall把必要调用的module,method,params都塞到行列里,然后传递到Native,

大家在回去Native 找到上文提到的八个首要接口,Native模块查询接口:global.nativeRequireModuleConfig和调用接口global.nativeFlushQueueImmediate,他们是在JS引擎(JSContext)领头化时,定义到全局变量的。

//RCTContextExecutor setUP//简化过的代码- setUp{ ... self->_context.context[@"nativeRequireModuleConfig"] = ^NSString *(NSString *moduleName) { NSArray *config = [weakBridge configForModuleName:moduleName]; return RCTJSONStringify(config, NULL); }; self->_context.context[@"nativeFlushQueueImmediate"] = ^(NSArray<NSArray *> *calls){ [weakBridge handleBuffer:calls batchEnded:NO]; }; ...}

[weakBridge handleBuffer:calls batchEnded:NO]; 经过意气风发多级传递,调用到_handleRequestNumber 中,用moduleID找到RCTModuleData,再用methodID 找到id<RCTBridgeMethod> method 然后在moduleData.instance实例中施行

- _handleRequestNumber:(NSUInteger)i moduleID:(NSUInteger)moduleID methodID:(NSUInteger)methodID params:(NSArray *)params{ RCTModuleData *moduleData = _moduleDataByID[moduleID]; id<RCTBridgeMethod> method = moduleData.methods[methodID]; [method invokeWithBridge:self module:moduleData.instance arguments:params];}

那边有须要再重申三回moduleData.instance 这一个地点。

- (id<RCTBridgeModule>)instance{ if (!_instance) { _instance = [_moduleClass new]; ... } return _instance;}

还记的前头BatchedBridge 开端化时的initModules吗

 //这里一个很有意思的地方,如果导出的类或其任何父类重写了init方法,或者类中有setBridge方法 //则React Native假设开发者期望这个导出模块在Bridge第一次初始化时实例化,否则怎么样,大家想想 if ([moduleClass instanceMethodForSelector:@selector] != objectInitMethod || [moduleClass instancesRespondToSelector:setBridgeSelector]) { module = [moduleClass new]; }

否则正是在客商真正调用时,在moduleData.instance里实例化,React Native已经懒到骨髓了。

RCTModuleData中各类函数的卷入 RCTModuleMethod里还可能有叁个优化点,JS传递到Native的参数须要进行响应的转换,RCTModuleMethod在调用函数只前,先预剖析一下,成立各样参数转换的block,缓存起来,那样调用时,就直接运用对应函数指针举行参数转变了,大体详细领会能够看

  • processMethodSignature函数。

前方大家为了直观,忽视了回调函数,alertWithArgs的第三个参数是一个JS回调函数,用来提醒顾客点击了哪个button,并打字与印刷出大器晚成行日志。

RCTAlertManager.alertWithArgs({message:'JS->Native Call',buttons:[{k1:'button1'},{k2:'button1'}]},function {console.log('RCTAlertManager.alertWithArgs() id:' + id +' v:' + v)});

回调函数的调用和直接从Native调用JS是大半的,再回头看看__nativeCall 函数大家忽略的部分

 __nativeCall(module, method, params, onFail, onSucc) { //Native接口最多支持两个回调 if (onFail || onSucc) { onFail && params.push(this._callbackID); this._callbacks[this._callbackID++] = onFail; onSucc && params.push(this._callbackID); this._callbacks[this._callbackID++] = onSucc; } this._queue[MODULE_IDS].push; this._queue[METHOD_IDS].push; this._queue[PARAMS].push; global.nativeFlushQueueImmediate(this._queue); this._queue = [[],[],[]]; this._lastFlush = now; }

能够见到把onFail,onSucc多个函数类型转变为两个数字ID插入到参数列表后边,并把函数函数缓存起来。从Native调用过来也比较轻巧了,传递过callbackID到JS,就足以举办到回调函数。

JS传递的参数仅仅是个整形ID,Native怎么着晓得这些ID就是个回调函数呢?

答案是和其余参数形似通过Native的函数具名,假诺开掘对应的参数是个block,则从JS传递过来的ID就是呼应的回调ID,把其转变为RCTResponseSenderBlock的闭包。

RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback)

到此甘休,我们曾经把全体JS->Natvie的流水生产线都走通了,梳理一下整整工艺流程。

betway体育app 13调用图.png

小结一下

  1. Native开端化时, Native生成要导出模块的名字列表,仅仅是模块名字列表, ModuleConfig
  1. 在React Native 的JS引擎开头化达成后,向JSContext注入ModuleConfig,赋值到JS全局变量 __fbBatchedBridgeConfig
  2. 还记得非常N->JS大使---JS对象BatchedBridge吗,BatchedBridge创制的时候会用__fbBatchedBridgeConfig变量里Native模块名字列表定义三个同名的JS对象,不过是多少个从未其他措施的空对象,只增添了多个拿走情势数组的get函数。那个时候最早化的操作已到位。
  3. 相当久非常久未来,有人用RCTAlertManager.alertWithArgs 调用了Native的代码,咳咳,那人是我,当时JS去拿到RCTAlertManager方法列表时,发掘是空的,就调用Native提供的查询函数nativeRequireModuleConfig 获取RCTAlertManager对象的详实的导出新闻,并定义成同名的JS函数,此函数转调到OC的落到实处
  4. 那会儿RCTAlertManager对应的JS对象才定义完整,JS找到了alertWithArgs函数,种种对应的JS函数都以叁个包装了调用__nativeCall的闭包,JS通过此函数转载到Native

能够看来,Native导出的配置表并不是在风姿浪漫开始就完整的流入JS并定义对应的JS对象,而是大器晚成味注入了二个模块名字,当运维时期有人调用的时候,才再从Native查询调用模块的详细陈设表,这种懒加运载飞机制化解了二个巨型的app导出的Api相当多,全部导入JS引致开端化时内部存款和储蓄器占用过大的标题。

[JS] 依据上述模块配置音讯生成 JS 模块

//BatchedBridge.js
const BatchedBridge = new MessageQueue(
  () => global.__fbBatchedBridgeConfig
);

  1. BatchedBridge 是 MessageQueue 的一个实例
  2. 先是次访问 BatchedBridge.RemoteModules 时 MessageQueue 会利用 global.__fbBatchedBridgeConfig 模块配置音讯来扭转 JS 模块

2.ReactBridge实现

通讯模型图中要调用WebKit的实现,少不了Bridge本条桥梁,因为Java是不能够一贯调用WebKit,可是足以Java通过JNIJNI再调用WebKit

JNI入口react/jni/OnLoad.cpp,通过RegisterNatives格局注册的,JNI_OnLoad其间注册了setGlobalVariablecallFunctionnative地面方法

//jni/react/jni/OnLoad.cpp
namespace bridge {
  ......

static void setGlobalVariable(JNIEnv* env, jobject obj, jstring propName, jstring jsonValue) {
  auto bridge = extractRefPtr<Bridge>(env, obj);
  bridge->setGlobalVariable(fromJString(env, propName), fromJString(env, jsonValue));
}
  ...
} // namespace bridge

extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  return initialize(vm, [] {
    // get the current env
    JNIEnv* env = Environment::current();
    registerNatives("com/facebook/react/bridge/ReadableNativeMap$ReadableNativeMapKeySetIterator", {
    ......
    registerNatives("com/facebook/react/bridge/ReactBridge", {
        makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create),
        makeNativeMethod(
          "loadScriptFromAssets", "(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
          bridge::loadScriptFromAssets),
        makeNativeMethod("loadScriptFromFile", bridge::loadScriptFromFile),
        makeNativeMethod("callFunction", bridge::callFunction),
        makeNativeMethod("invokeCallback", bridge::invokeCallback),
        makeNativeMethod("setGlobalVariable", bridge::setGlobalVariable),
        makeNativeMethod("supportsProfiling", bridge::supportsProfiling),
        makeNativeMethod("startProfiler", bridge::startProfiler),
        makeNativeMethod("stopProfiler", bridge::stopProfiler),
        makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
        makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),

    });
}

能够见见setGlobalVariable真正调用的是Bridge.cpp里面包车型大巴setGlobalVariable:

//jni/react/Bridge.h
class JSThreadState;
class Bridge : public Countable {
public:
  typedef std::function<void(std::vector<MethodCall>, bool isEndOfBatch)> Callback;

  Bridge(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Callback callback);
  virtual ~Bridge();

  /**
   * Flush get the next queue of changes.
   */
  void flush();

  /**
   * Executes a function with the module ID and method ID and any additional
   * arguments in JS.
   */
  void callFunction(const double moduleId, const double methodId, const folly::dynamic& args);

  /**
   * Invokes a callback with the cbID, and optional additional arguments in JS.
   */
  void invokeCallback(const double callbackId, const folly::dynamic& args);

  void executeApplicationScript(const std::string& script, const std::string& sourceURL);
  void setGlobalVariable(const std::string& propName, const std::string& jsonValue);
  bool supportsProfiling();
  void startProfiler(const std::string& title);
  void stopProfiler(const std::string& title, const std::string& filename);
  void handleMemoryPressureModerate();
  void handleMemoryPressureCritical();
private:
  Callback m_callback;
  std::unique_ptr<JSThreadState> m_threadState;
  // This is used to avoid a race condition where a proxyCallback gets queued after ~Bridge(),
  // on the same thread. In that case, the callback will try to run the task on m_callback which
  // will have been destroyed within ~Bridge(), thus causing a SIGSEGV.
  std::shared_ptr<bool> m_destroyed;
};

void Bridge::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
  m_threadState->setGlobalVariable(propName, jsonValue);
}

最终调用的是JSThreadState里面包车型大巴setGlobalVariable:

//jni/react/Bridge.cpp
class JSThreadState {
public:
  JSThreadState(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Bridge::Callback&& callback) :
    m_callback(callback)
  {
    m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON, bool isEndOfBatch) {
      m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
    });
  }

  void setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
    m_jsExecutor->setGlobalVariable(propName, jsonValue);
  }

private:
  std::unique_ptr<JSExecutor> m_jsExecutor;
  Bridge::Callback m_callback;
};

说起底调用JSExecutor里面setGlobalVariable:

//jni/react/JSCExecutor.h
class JSCExecutor : public JSExecutor, public JSCWebWorkerOwner {
public:
  /**
   * Should be invoked from the JS thread.
   */
  explicit JSCExecutor(FlushImmediateCallback flushImmediateCallback);
  ~JSCExecutor() override;

  virtual void executeApplicationScript(
    const std::string& script,
    const std::string& sourceURL) override;
  virtual std::string flush() override;
  virtual std::string callFunction(
    const double moduleId,
    const double methodId,
    const folly::dynamic& arguments) override;
  virtual std::string invokeCallback(
    const double callbackId,
    const folly::dynamic& arguments) override;
  virtual void setGlobalVariable(
    const std::string& propName,
    const std::string& jsonValue) override;
  virtual bool supportsProfiling() override;
  virtual void startProfiler(const std::string &titleString) override;
  virtual void stopProfiler(const std::string &titleString, const std::string &filename) override;
  virtual void handleMemoryPressureModerate() override;
  virtual void handleMemoryPressureCritical() override;

  void flushQueueImmediate(std::string queueJSON);
  void installNativeHook(const char *name, JSObjectCallAsFunctionCallback callback);
  virtual void onMessageReceived(int workerId, const std::string& message) override;
  virtual JSGlobalContextRef getContext() override;
  virtual std::shared_ptr<JMessageQueueThread> getMessageQueueThread() override;
};

} }

//jni/react/JSCExecutor.cpp
void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
  auto globalObject = JSContextGetGlobalObject(m_context);
  String jsPropertyName(propName.c_str());

  String jsValueJSON(jsonValue.c_str());
  auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);

  JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
}

从JSCExecutor::setGlobalVariable中能够看见java层传过来的JSON串(包含Native Modules和JS Modules卡塔尔和属性名(__fbBatchedBridgeConfig卡塔尔,赋值给全局变量globalObject,那些全局变量通过JSContextGetGlobalObject获取到,在JSCExecutor.h头文件中:

#include <memory>
#include <unordered_map>
#include <JavaScriptCore/JSContextRef.h>
#include "Executor.h"
#include "JSCHelpers.h"
#include "JSCWebWorker.h"

JavaScriptCore/JSContextRef.h是Webkit那些库中的文件,在ReactAndroid下的build.gradle中有目录这么些库,到build.gradle中看看:

betway体育app 14

ReactAndroid.png

//ReactAndroid/build.gradle
task downloadJSCHeaders(type: Download) {
    def jscAPIBaseURL = 'https://svn.webkit.org/repository/webkit/!svn/bc/174650/trunk/Source/JavaScriptCore/API/'
    def jscHeaderFiles = ['JSBase.h', 'JSContextRef.h', 'JSObjectRef.h', 'JSRetainPtr.h', 'JSStringRef.h', 'JSValueRef.h', 'WebKitAvailability.h']
    def output = new File(downloadsDir, 'jsc')
    output.mkdirs()
    src(jscHeaderFiles.collect { headerName -> "$jscAPIBaseURL$headerName" })
    onlyIfNewer true
    overwrite false
    dest output
}

开荒jscAPIBaseU昂科威L那几个url能够找到JSContextRef.h头文件:

//JavaScriptCore/API/JSContextRef.h
...
/*!
@function
@abstract Gets the global object of a JavaScript execution context.
@param ctx The JSContext whose global object you want to get.
@result ctx's global object.
*/
JS_EXPORT JSObjectRef JSContextGetGlobalObject(JSContextRef ctx);
...

JSCExecutor::setGlobalVariable最终给JS的全局变量设置二个属性__fbBatchedBridgeConfig,那些天性的值便是JavaScriptModule,Java调用JS的时候附属性中找找,看下callFunction:

//jni/react/JSCExecutor.cpp
std::string JSCExecutor::callFunction(const double moduleId, const double methodId, const folly::dynamic& arguments) {
  // TODO:  Make this a first class function instead of evaling. #9317773
  std::vector<folly::dynamic> call{
    (double) moduleId,
    (double) methodId,
    std::move(arguments),
  };
  return executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call));
}

static std::string executeJSCallWithJSC(
    JSGlobalContextRef ctx,
    const std::string& methodName,
    const std::vector<folly::dynamic>& arguments) {
  #ifdef WITH_FBSYSTRACE
  FbSystraceSection s(
      TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.executeJSCall",
      "method", methodName);
  #endif

  // Evaluate script with JSC
  folly::dynamic jsonArgs(arguments.begin(), arguments.end());
  auto js = folly::to<folly::fbstring>(
      "__fbBatchedBridge.", methodName, ".apply(null, ",
      folly::toJson(jsonArgs), ")");
  auto result = evaluateScript(ctx, String(js.c_str()), nullptr);
  return Value(ctx, result).toJSONString();
}

最终以

__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})的样式拼接叁个applyJavascript施行语句,最终调用jni/react/JSCHelpers.cppevaluateScript的来试行这几个讲话,落成BridgeJavascript的调用,JSCHelpersWebKit的一些API做了打包,它承当最终调用WebKit

//jni/react/JSCHelpers.h
namespace facebook {
namespace react {

void installGlobalFunction(
    JSGlobalContextRef ctx,
    const char* name,
    JSObjectCallAsFunctionCallback callback);

JSValueRef makeJSCException(
    JSContextRef ctx,
    const char* exception_text);

JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source);

} }

Bridge层的调用进程: OnLoad.cpp->Bridge.cpp->JSCExecutor.cpp->JSCHelpers.cpp->WebKit

}

injectJSONConfiguration

该职责将上三个职责建构的 JSON 注入到 Executor 中。
上边是二个 JSON 示例,由于实在的指标太大,这里只截取了日前的有的:
betway体育app 15
JSON 里面纵使全部暴暴光来的模块音信。

React Native 已经临盆近一年时间了,这两日也在切磋iOS下用js写app的框架,从徘徊和迟疑中,最终依旧选定React Native,她宛若是隐若显的美女相符,想要下决心追到,然而不轻易。要想把他运用的已存在的有早晚体积的app中,更是无可争辩,让笔者先把他里外都询问领悟,在分享一下靠边利用到现存app的方案,那是深远React Native体系的率先篇,后续会三番一次享受应用进度中的一些认知。

[JS] NativeModules 生成

由在此以前边的步骤,native 注入到 JSCExecutor 中的 global.__fbBatchedBridgeConfig 在 BatchedBridge.RemoteModules 属性第贰遍被访谈的时候被分析成完全的 module,下一步则是将这一个 module 归入NativeModules 对象并供外界访问。
除此以外,这里有个八个小拍卖,native 注入的模块平日以 RCT 恐怕 PAJEROK 为前缀,此处会将前缀干掉(以下代码未包蕴)。

//NativeModules.js
const BatchedBridge = require('BatchedBridge');
const RemoteModules = BatchedBridge.RemoteModules;
//代码省略...
/**
 * 这里也是一个 lazy getters
 * Define lazy getters for each module.
 * These will return the module if already loaded, or load it if not.
 */
const NativeModules = {};
Object.keys(RemoteModules).forEach((moduleName) => {
  Object.defineProperty(NativeModules, moduleName, {
    configurable: true,
    enumerable: true,
    get: () => {
      let module = RemoteModules[moduleName];
      //代码省略...
      Object.defineProperty(NativeModules, moduleName, {
        configurable: true,
        enumerable: true,
        value: module,
      });
      return module;
    },
  });
});

4、Shadow Queue: ”com.facebook.React.ShadowQueue”,首要管理RCTUIManager相关业务;

loadSource

该职责担负加载 JS 代码到内部存储器中。和前素不相识龙活虎致,就算 JS 地址是 UEvoqueL 的格局,就由此网络去读取,要是是文本的花样,则经过读本地磁盘文件的法门读取。

Native 与 JS的并行调用

0.17本子的React Native JS引擎已经全副行使的是iOS自带的JavaScriptCore,在JSContext提供的Native与js互相调用的底子上,封装出了和睦的互调方法。上面是一张布局图

betway体育app 16架构图.png

[Native] 将模块新闻注入到 JSCExecutor 中的全局变量

// RCTBatchedBridge.m
- (void)injectJSONConfiguration:(NSString *)configJSON
                     onComplete:(void (^)(NSError *))onComplete
{
  if (!_valid) {
    return;
  }

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

__fbBatchedBridgeConfig 的布局如下

{
    "remoteModuleConfig": [
        [
            "模块名称",
            {
                属性对象(键值对,值类型不限)
            },
            [
                方法列表
            ],
            [
                Promise 方法 index(方法列表中哪些方法是异步的)
            ]
        ],

  1. 各样模块的 index 就是这些模块的 moduleID
  2. 各样方法的 index 正是那一个点子的 methodID
  3. 末尾输出到 remoteModuleConfig 中的模块并不是持有的 Module,有的会被过滤掉,所以输出里有的是 null

如上构造的变型在 RCTModuleData config 方法中:

// RCTModuleData.m
- (NSArray *)config
{
  //......
  NSMutableArray *config = [NSMutableArray new];
  [config addObject:self.name]; // 模块名称(字符串)
  if (constants.count) {
    [config addObject:constants]; // 属性对象(对象)
  }
  if (methods) {
    [config addObject:methods]; // 方法列表(数组)
    if (asyncMethods) {
      [config addObject:asyncMethods]; // Promise 方法列表(数组)
    }
  }
  RCT_PROFILE_END_EVENT(RCTProfileTagAlways, [NSString stringWithFormat:@"[RCTModuleData config] %@", _moduleClass], nil);
  return config;
}

举个例证

{
    "remoteModuleConfig": [
        null,
        [
            "RCTAccessibilityManager",
            [
                "setAccessibilityContentSizeMultipliers",
                "getMultiplier",
                "getCurrentVoiceOverState"
            ]
        ],
        [
            "RCTViewManager",
            {
                "forceTouchAvailable": false
            }
        ],
        ...
        [
            "RCTClipboard",
            [
                "setString",
                "getString"
            ],
            [
                1
            ]
        ],
        ...
        [
            "RCTPushNotificationManager",
            {
                "initialNotification": null
            },
            [
                "setApplicationIconBadgeNumber",
                "getApplicationIconBadgeNumber",
                "requestPermissions",
                "abandonPermissions",
                "checkPermissions",
                "presentLocalNotification",
                "scheduleLocalNotification",
                "cancelAllLocalNotifications",
                "cancelLocalNotifications",
                "getInitialNotification",
                "getScheduledLocalNotifications",
                "addListener",
                "removeListeners"
            ],
            [
                2,
                9
            ]
        ]

[moduleMethods addObject:moduleMethod];

JS 运行进程

React Native 的 iOS 端代码是一向从 Xcode IDE 里运营的。在运行时,首先要对代码举办编写翻译,不出意外,在编写翻译后会弹出三个限令行窗口,那么些窗口正是通过 Node.js 运行的 development server

标题是以此命令行是怎么运转起来的吧?实际上,Xcode 在 Build Phase 的尾声二个等级对此做了陈设:
betway体育app 17

由此,代码编写翻译后,就能够施行 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: 通过拜望 能够看出内部存款和储蓄器中缓存的保有编写翻译后的文本名及文件内容,如:
betway体育app 18

本篇详细深入分析下React Native 中 Native和JS的相互调用的法则解析。以前bang的稿子已经介绍过,本文从代码层面更加深刻的来传授,深入分析基于 React Native 0.17.0 版本, RubiconN在高效腾飞,个中的源委已和前边的旧版本有一点点分裂

[JS] JS 模块的扭转

率先,JS 模块的布局

{
    name: "模块名称",
    {
        方法名:方法实现(区分是否 Promise(Sync)) 类型
        属性名:
        moduleID: 模块Index
    }
}

现实蕴含几块:

  • 模块名
  • 模块属性
  • moduleID
  • 模块方法

率先步 遍历全数 remoteModules,即 global.__fbBatchedBridgeConfig 列表:

// MessageQueue.js
_genModules(remoteModules) {
  const modules = {};

  remoteModules.forEach((config, moduleID) => {
    const info = this._genModule(config, moduleID);
    if (info) {
      modules[info.name] = info.module;
    }
  });

  return modules;
}

其次步 深入解析出每种 Module 的音信,蕴涵模块名、属性对象、方法列表、异步(Promise卡塔尔(قطر‎方法列表、syncHooks(这些和 native 布局对应不上,有望弃用了)

// MessageQueue.js
let moduleName, constants, methods, asyncMethods, syncHooks;
if (moduleHasConstants(config)) {
  [moduleName, constants, methods, asyncMethods, syncHooks] = config;
} else {
  [moduleName, methods, asyncMethods, syncHooks] = config;
}

其三步 依照办法列表、属性对象生成 module 对象

// MessageQueue.js
// 初始化新 module
const module = {};
methods && methods.forEach((methodName, methodID) => {
  const isAsync = asyncMethods && arrayContains(asyncMethods, methodID);
  const isSyncHook = syncHooks && arrayContains(syncHooks, methodID);
  invariant(!isAsync || !isSyncHook, 'Cannot have a method that is both async and a sync hook');
  // 方法类型,Async 方法调用会返回 Promise 对象
  const methodType = isAsync ? MethodTypes.remoteAsync :
      isSyncHook ? MethodTypes.syncHook :
      MethodTypes.remote;
  // 生成方法
  module[methodName] = this._genMethod(moduleID, methodID, methodType);
});
// 将属性浅拷贝到 module 上
Object.assign(module, constants);

至于艺术的改变:

  1. 实质上都以将艺术的实际操作代理给了 __nativeCall(moduleID, methodID, args, onFail, onSucc)
  2. 对此 async 方法会用 Promise 对象包装一下,然后 onFail 的时候 reject,以致 onSucc 的时候 resolve(不过看代码里onFail 和 onSucc 的职分不等同,是还是不是有标题需求越发证实)
  3. 关于 __nativeCall 的逻辑后续写风流洒脱篇JS代码执行后的剖释来深入分析
// MessageQueue.js
_genMethod(module, method, type) {
    let fn = null;
    const self = this;
    if (type === MethodTypes.remoteAsync) {
      fn = function(...args) {
        return new Promise((resolve, reject) => {
          self.__nativeCall(
            module,
            method,
            args,
            (data) => {
              resolve(data);
            },
            (errorData) => {
              var error = createErrorFromErrorData(errorData);
              reject(error);
            });
        });
      };
    } else if (type === MethodTypes.syncHook) {
      return function(...args) {
        return global.nativeCallSyncHook(module, method, args);
      };
    } else {
      fn = function(...args) {
        const lastArg = args.length > 0 ? args[args.length - 1] : null;
        const secondLastArg = args.length > 1 ? args[args.length - 2] : null;
        const hasSuccCB = typeof lastArg === 'function';
        const hasErrorCB = typeof secondLastArg === 'function';
        hasErrorCB && invariant(
          hasSuccCB,
          'Cannot have a non-function arg after a function arg.'
        );
        const numCBs = hasSuccCB + hasErrorCB;
        const onSucc = hasSuccCB ? lastArg : null;
        const onFail = hasErrorCB ? secondLastArg : null;
        args = args.slice(0, args.length - numCBs);
        return self.__nativeCall(module, method, args, onFail, onSucc);
      };
    }
    fn.type = type;
    return fn;
  }

起始化流程牵涉多少个部分:

Native 运转进度

Native 摆正是一个 iOS 程序,程序入口是 main 函数,像日常同样,它肩负对应用程序做开头化。

除开 main 函数之外,AppDelegate 也是二个十一分主要的类,它首要用于做一些大局的决定。在应用程序运转未来,个中的 didFinishLaunchingWithOptions 方法会被调用,在此个艺术中,首要做了几件事:

  • 概念了 JS 代码所在的职务,它在 dev 景况下是叁个 URubiconL,通过 development server 访谈;在生养意况下则从磁盘读取,当然前提是早已手动生成过了 bundle 文件;
  • 开创了二个 RCTRootView 对象,该类世襲于 UIView,处于程序有所 View 的最外层;
  • 调用 RCTRootView 的 initWithBundleURL 方法。在该方法中,创立了 bridge 对象。看名就可以见到意思,bridge 起着七个端之间的桥接功能,个中的确行事的是类正是老品牌的 RCTBatchedBridge

RCTBatchedBridge 是发轫化时通讯的基本,大家任重先生而道远关怀的是 start 方法。在 start 方法中,会创立三个 GCD 线程,该线程通过串行队列调整了以下多少个至关心重视要的天职。

作为初篇,先创立三个演示工程,未来的享用都以那一个工程为底子。前段时间以此工程还超级粗略,main.js的执教能够下载这里的代码

[Native] iOS main 函数推行

先是,iOS 应用软件 运营后先进行的是 main 函数,此处十分少说。

http://tadeuzagallo.com/blog/react-native-bridge/

是时候战胜心中的惊悸,早先掀裙子了 ,来拜会main.jsbundle中的JS代码

上文Natvie调用JS的路子到了 __fbBatchedBridge.callFunctionReturnFlushedQueue js代码这一步,demo工程中,大家协调写的index.ios.js 独有区区几行,去Node Server转黄金时代圈或React Native的预编写翻译之后,竟然发出了1.3M,近5W行JS代码的main.jsbundle 文件,对于极端同学来讲,几乎是后生可畏座南昆山。不要惧怕,大家生机勃勃并寻找此中的神妙。

三回九转追踪js代码中的 __fbBatchedBridge

//main.js__d('BatchedBridge',function(global, require, module, exports) { 'use strict'; var MessageQueue=require('MessageQueue'); var BatchedBridge=new MessageQueue( __fbBatchedBridgeConfig.remoteModuleConfig, __fbBatchedBridgeConfig.localModulesConfig); Object.defineProperty(global,'__fbBatchedBridge',{value:BatchedBridge}); module.exports=BatchedBridge;});

我们开掘这段JS代码中有那句 Object.defineProperty(global,'__fbBatchedBridge',{value:BatchedBridge});

[Native] 初始化 Bridge(在 ViewController 中)

self.rctRootView = 
    [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                moduleName:@"TiebaNext"
                         initialProperties:nil
                             launchOptions:nil];

2)调用宏RCT_EXPORT_MODULE() & RCT_EXPORT_METHOD() ,如

肥皂V 2016 1.3

betway体育app 19

2、配置native module(RCTBatchedBridge moduleConfig)

本文由必威发布于必威-前端,转载请注明出处:来声明该模块需要暴露给,betway体育app:Native层

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。