以添加CalendarManager為例
.h
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>
#import <Foundation/Foundation.h>
@interface CalendarManager : NSObject<RCTBridgeModule>
@end
.m
@implementation CalendarManager
- (instancetype)init {
if (self = [super init]) {
}
return self;
}
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
// Date is ready to use!
NSLog(@"location1-----%@",location);
}
RCT_EXPORT_METHOD(fangshufeng:(NSString *)name testTwo:(NSString *)location)
{
// Date is ready to use!
NSLog(@"location3-----%@",location);
}
RCT_REMAP_METHOD(fangshufengJsName,
addEvent:(NSString *)name testTwo:(NSString *)location hh:(NSString*)jj) {
NSLog(@"location4-----%@",location);
}
@end
運(yùn)行xcode,在下面的地方打上斷點(diǎn)

輸出configJSON,只保留CalendarManager相關(guān)的并且json一下就是
{
"remoteModuleConfig": [
[
"CalendarManager",
null,
[
"addEvent", // 這里對(duì)應(yīng)的就是第一個(gè)方法addEvent:(NSString *)name location:(NSString *)location 下面兩個(gè)分別對(duì)應(yīng)第二第三個(gè)
"fangshufeng",
"fangshufengJsName"
],
null,
null
],
...
]
}
這是在debug環(huán)境的情況下傳的是完整配置信息,而在release環(huán)境下這個(gè)配置信息就只有module信息,而其他的方法信息都是通過(guò)懶加載的形式得到
oc通過(guò)方法將jsonstring傳給js
[_javaScriptExecutor injectJSONText:configJSON
asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
callback:onComplete];
js如何接收呢
在NativeModules.js中
const bridgeConfig = global.__fbBatchedBridgeConfig;
通過(guò)一個(gè)global的key__fbBatchedBridgeConfig將數(shù)據(jù)給到j(luò)s
由
let NativeModules : {[moduleName: string]: Object} = {};
可以知道NativeModules本身是一個(gè)對(duì)象
我們?cè)?code>debugger
(bridgeConfig.remoteModuleConfig || []).forEach((config: ModuleConfig, moduleID: number) => {
// Initially this config will only contain the module name when running in JSC. The actual
// configuration of the module will be lazily loaded.
const info = genModule(config, moduleID);
debugger;
if (!info) {
return;
}
if (info.module) {
NativeModules[info.name] = info.module;
}
// If there's no module config, define a lazy getter
else {
defineLazyObjectProperty(NativeModules, info.name, {
get: () => loadModule(info.name, moduleID)
});
}
});
}
可以過(guò)濾出CalendarManager如下圖所示

具體看看這個(gè)方法做的事情可以知道每個(gè)item都會(huì)經(jīng)過(guò)genModule方法,先來(lái)看看
function genModule(config: ?ModuleConfig, moduleID: number): ?{name: string, module?: Object} {
if (!config) {
return null;
}
const [moduleName, constants, methods, promiseMethods, syncMethods] = config;
invariant(!moduleName.startsWith('RCT') && !moduleName.startsWith('RK'),
'Module name prefixes should\'ve been stripped by the native side ' +
'but wasn\'t for ' + moduleName);
if (!constants && !methods) {
// Module contents will be filled in lazily later
return { name: moduleName };
}
const module = {};
methods && methods.forEach((methodName, methodID) => {
const isPromise = promiseMethods && arrayContains(promiseMethods, methodID);
const isSync = syncMethods && arrayContains(syncMethods, methodID);
invariant(!isPromise || !isSync, 'Cannot have a method that is both async and a sync hook');
const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
module[methodName] = genMethod(moduleID, methodID, methodType);
});
Object.assign(module, constants);
if (__DEV__) {
BatchedBridge.createDebugLookup(moduleID, moduleName, methods);
}
return { name: moduleName, module };
}
這個(gè)又用到了genMethod方法,好吧先來(lái)看看
function genMethod(moduleID: number, methodID: number, type: MethodType) {
let fn = null;
if (type === 'promise') {
fn = function(...args: Array<any>) {
return new Promise((resolve, reject) => {
BatchedBridge.enqueueNativeCall(moduleID, methodID, args,
(data) => resolve(data),
(errorData) => reject(createErrorFromErrorData(errorData)));
});
};
} else if (type === 'sync') {
fn = function(...args: Array<any>) {
return global.nativeCallSyncHook(moduleID, methodID, args);
};
} else {
fn = function(...args: Array<any>) {
console.log('----', args , 'moduleID:--' + moduleID , 'methodID:---' + methodID);
const lastArg = args.length > 0 ? args[args.length - 1] : null;
const secondLastArg = args.length > 1 ? args[args.length - 2] : null;
const hasSuccessCallback = typeof lastArg === 'function';
const hasErrorCallback = typeof secondLastArg === 'function';
hasErrorCallback && invariant(
hasSuccessCallback,
'Cannot have a non-function arg after a function arg.'
);
const onSuccess = hasSuccessCallback ? lastArg : null;
const onFail = hasErrorCallback ? secondLastArg : null;
const callbackCount = hasSuccessCallback + hasErrorCallback;
args = args.slice(0, args.length - callbackCount);
BatchedBridge.enqueueNativeCall(moduleID, methodID, args, onFail, onSuccess);
};
}
fn.type = type;
return fn;
}
方法分為三種,由oc傳給js的時(shí)候也有體現(xiàn)的這里我們先直接看aync情況的,這里做了三件事情
- 定義了一個(gè)接收多個(gè)參數(shù)額方法;
- 在參數(shù)列表中取出最后的兩個(gè)作為js的回調(diào);
- 最終調(diào)用的是原生的方法這里又引出了另一個(gè)方法
BatchedBridge.enqueueNativeCall
enqueueNativeCall(moduleID: number, methodID: number, params: Array<any>, onFail: ?Function, onSucc: ?Function) {
if (onFail || onSucc) {
if (__DEV__) {
const callId = this._callbackID >> 1;
this._debugInfo[callId] = [moduleID, methodID];
if (callId > DEBUG_INFO_LIMIT) {
delete this._debugInfo[callId - DEBUG_INFO_LIMIT];
}
}
onFail && params.push(this._callbackID);
/* $FlowFixMe(>=0.38.0 site=react_native_fb,react_native_oss) - Flow error
* detected during the deployment of v0.38.0. To see the error, remove
* this comment and run flow */
this._callbacks[this._callbackID++] = onFail;
onSucc && params.push(this._callbackID);
/* $FlowFixMe(>=0.38.0 site=react_native_fb,react_native_oss) - Flow error
* detected during the deployment of v0.38.0. To see the error, remove
* this comment and run flow */
this._callbacks[this._callbackID++] = onSucc;
}
if (__DEV__) {
global.nativeTraceBeginAsyncFlow &&
global.nativeTraceBeginAsyncFlow(TRACE_TAG_REACT_APPS, 'native', this._callID);
}
this._callID++;
this._queue[MODULE_IDS].push(moduleID);
this._queue[METHOD_IDS].push(methodID);
if (__DEV__) {
// Any params sent over the bridge should be encodable as JSON
JSON.stringify(params);
// The params object should not be mutated after being queued
deepFreezeAndThrowOnMutationInDev((params:any));
}
this._queue[PARAMS].push(params);
const now = new Date().getTime();
if (global.nativeFlushQueueImmediate &&
(now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS ||
this._inCall === 0)) {
var queue = this._queue;
this._queue = [[], [], [], this._callID];
this._lastFlush = now;
global.nativeFlushQueueImmediate(queue);
}
Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length);
if (__DEV__ && this.__spy && isFinite(moduleID)) {
this.__spy(
{ type: TO_NATIVE,
module: this._remoteModuleTable[moduleID],
method: this._remoteMethodTable[moduleID][methodID],
args: params }
);
} else if (this.__spy) {
this.__spy({type: TO_NATIVE, module: moduleID + '', method: methodID, args: params});
}
}
這個(gè)方法一共做了以下幾件事情:
判斷如果后面兩個(gè)參數(shù)有回調(diào)的話就會(huì)為每個(gè)回調(diào)生成一個(gè)唯一的ID這里叫做
_callbackID,并將js方法裝進(jìn)一個(gè)_callbacks的數(shù)組中;-
這里有個(gè)
_queue對(duì)象他的結(jié)構(gòu)如下_queue: [Array<number>, Array<number>, Array<any>, number]; 在js中分別以`MODULE_IDS` `METHOD_IDS` 和 `PARAMS`來(lái)表示數(shù)組的第0、1、2號(hào)元素,之所以這樣命名是達(dá)到為了見(jiàn)名之意的效果完成了第一步的
_callbackID的映射,在分別吧對(duì)應(yīng)的模塊idmoduleID、方法idmethodID和參數(shù)params分別裝進(jìn)_queue對(duì)應(yīng)的數(shù)組中this._queue[MODULE_IDS].push(moduleID); this._queue[METHOD_IDS].push(methodID); this._queue[PARAMS].push(params); 回調(diào)原生的
global.nativeFlushQueueImmediate方法,對(duì)應(yīng)原生的
```
context[@"nativeFlushQueueImmediate"] = ^(NSArray<NSArray *> *calls){
RCTJSCExecutor *strongSelf = weakSelf;
if (!strongSelf.valid || !calls) {
return;
}
RCT_PROFILE_BEGIN_EVENT(RCTProfileTagAlways, @"nativeFlushQueueImmediate", nil);
[strongSelf->_bridge handleBuffer:calls batchEnded:NO];
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"js_call");
};
```
值得注意的是這里只是做方法的申明,并沒(méi)有調(diào)用。
那我們?cè)诨氐阶铋_(kāi)始的genModule方法,也就是說(shuō)經(jīng)過(guò)genModule方法以后以我們的CalendarManager來(lái)說(shuō)的話也就是返回一個(gè)下面形式的對(duì)象
{
name:"CalendarManager",
{
addEvent: {
fn(...){},
type:"async"
},
fangshufeng: {
fn(...){},
type:"async"
},
fangshufengJsName: {
fn(...){},
type:"async"
}
}
}
再回到一開(kāi)始的方法
if (info.module) {
NativeModules[info.name] = info.module;
}
從而經(jīng)過(guò)一大圈的變換最后得到的結(jié)果就是返回一個(gè)NativeModules對(duì)象,還是以CalendarManager為例,得到的NativeModules的對(duì)象如下
CalendarManager:
{
addEvent: {
fn(...){},
type:"async"
},
fangshufeng: {
fn(...){},
type:"async"
},
fangshufengJsName: {
fn(...){},
type:"async"
}
}
我們自定義的CalendarManager作為了NativeModules的一個(gè)屬性,CalendarManager所對(duì)應(yīng)的方法列表成了CalendarManager屬性
這里只是用CalendarManager為例子來(lái)講的,實(shí)際上最后NativeModules是有很多的類(lèi)似CalendarManager的屬性

也就是說(shuō)每一個(gè)原生模塊都對(duì)應(yīng)NativeModules一個(gè)屬性
到這里也就說(shuō)完了原生創(chuàng)建一個(gè)模塊到j(luò)s的演變過(guò)程,我們來(lái)總結(jié)一下,當(dāng)我們要?jiǎng)?chuàng)建一個(gè)原生模塊的時(shí)候需要做些什么;
要繼承<RCTBridgeModule>這個(gè)協(xié)議;
-
要在.m中加入RCT_EXPORT_MODULE();還記得之前那個(gè)發(fā)給js的那個(gè)原生模塊的jsonString吧這個(gè)目的其實(shí)是要將當(dāng)前模塊加入配置表中也就是下面的這個(gè)方法
void RCTRegisterModule(Class moduleClass) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ RCTModuleClasses = [NSMutableArray new]; }); RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)], @"%@ does not conform to the RCTBridgeModule protocol", moduleClass); // Register module [RCTModuleClasses addObject:moduleClass]; } -
對(duì)于要暴露給js的oc方法需要使用
RCT_EXPORT_METHOD這個(gè)宏RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)展開(kāi)以后就是下面這個(gè)樣子
+ (NSArray<NSString *> *)__rct_export__300 { return @[@"", @"addEvent:(NSString *)name location:(NSString *)location "]; } - (void)addEvent:(NSString *)name location:(NSString *)location;到后面要查找方法列表的時(shí)候就是通過(guò)
__rct_export__前綴來(lái)查找的,查找的方法如下
- (NSArray<id<RCTBridgeMethod>> *)methods
{
if (!_methods) {
NSMutableArray<id<RCTBridgeMethod>> *moduleMethods = [NSMutableArray new];
if ([_moduleClass instancesRespondToSelector:@selector(methodsToExport)]) {
[moduleMethods addObjectsFromArray:[self.instance methodsToExport]];
}
unsigned int methodCount;
Class cls = _moduleClass;
while (cls && cls != [NSObject class] && cls != [NSProxy class]) {
Method *methods = class_copyMethodList(object_getClass(cls), &methodCount);
for (unsigned int i = 0; i < methodCount; i++) {
Method method = methods[i];
SEL selector = method_getName(method);
if ([NSStringFromSelector(selector) hasPrefix:@"__rct_export__"]) {
IMP imp = method_getImplementation(method);
NSArray<NSString *> *entries =
((NSArray<NSString *> *(*)(id, SEL))imp)(_moduleClass, selector);
id<RCTBridgeMethod> moduleMethod =
[[RCTModuleMethod alloc] initWithMethodSignature:entries[1]
JSMethodName:entries[0]
moduleClass:_moduleClass];
[moduleMethods addObject:moduleMethod];
}
}
free(methods);
cls = class_getSuperclass(cls);
}
_methods = [moduleMethods copy];
}
return _methods;
}
這個(gè)方法做了兩個(gè)事情,一個(gè)是查找已__rct_export__開(kāi)頭的方法,一個(gè)是以__rct_export__方法開(kāi)頭的第一個(gè)參數(shù)為js方法的名稱(chēng)。
- 由3可知以這個(gè)宏
RCT_EXPORT_METHOD的話默認(rèn)給js的方法名稱(chēng)是參數(shù)的第一位,可以知道都是空的,可是我們最后是需要給js一個(gè)方法一個(gè)名稱(chēng)的這里oc做了默認(rèn)處理了
- (NSString *)JSMethodName
{
NSString *methodName = _JSMethodName;
if (methodName.length == 0) {
methodName = _methodSignature;
NSRange colonRange = [methodName rangeOfString:@":"];
if (colonRange.location != NSNotFound) {
methodName = [methodName substringToIndex:colonRange.location];
}
methodName = [methodName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
RCTAssert(methodName.length, @"%@ is not a valid JS function name, please"
" supply an alternative using RCT_REMAP_METHOD()", _methodSignature);
}
return methodName;
}
也就是如果JSMethodName的長(zhǎng)度為空的話就會(huì)截取第二個(gè)參數(shù)的:以前的字符串作為方法,也就是
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)對(duì)應(yīng)addEvent,RCT_EXPORT_METHOD(fangshufeng:(NSString *)name testTwo:(NSString *)location)對(duì)應(yīng)fangshufeng,那這樣的話問(wèn)題就來(lái)了,萬(wàn)一還有個(gè)這樣的方法RCT_EXPORT_METHOD(fangshufeng:(NSString *)name)按照默認(rèn)的規(guī)則豈不是就和RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)沖突了嗎,rn為了避免這種情況,我們可以使用這個(gè)宏RCT_REMAP_METHOD,也就是這種用法
RCT_REMAP_METHOD(fangshufengJsName,
addEvent:(NSString *)name testTwo:(NSString *)location hh:(NSString*)jj) {
NSLog(@"location4-----%@",location);
}
這樣的話之前那個(gè)數(shù)組的參數(shù)第一個(gè)值就有值了對(duì)應(yīng)fangshufengJsName
- 在研究js源碼的時(shí)候我們發(fā)現(xiàn),js對(duì)參數(shù)的解析是有要注意的地方的,如果原生想要使用js的回調(diào)的話只能放在參數(shù)的后兩位
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location action:(RCTResponseSenderBlock)action)
{
// Date is ready to use!
NSLog(@"location1-----%@",location);
}
這樣的話就是錯(cuò)的
RCT_EXPORT_METHOD(addEvent:(NSString *)name action:(RCTResponseSenderBlock)action location:(NSString *)location)
{
// Date is ready to use!
NSLog(@"location1-----%@",location);
}
接下來(lái)將要講的是當(dāng)自定義的模塊方法被調(diào)用時(shí)數(shù)據(jù)是如何流動(dòng)的。
首先我們先把CalendarManager用起來(lái)
import React,{Component} from 'react';
import {
NativeModules,
Text,
TouchableOpacity,
NativeAppEventEmitter,
View,
} from 'react-native'
import FadeInView from './FadeInView'
let CalendarManager = NativeModules.CalendarManager;
export default class TestAnimal extends Component {
render() {
return (
<TouchableOpacity
onPress={() => {CalendarManager.fangshufeng('name','ggg')}}
>
<FadeInView style={{width: 250, height: 50, backgroundColor: 'powderblue'}}
>
<Text style={{fontSize: 28, textAlign: 'center', margin: 10}}>Fading in</Text>
</FadeInView>
</TouchableOpacity>
)
}
}
運(yùn)行的結(jié)果

點(diǎn)擊fading in也就是調(diào)用fangshufeng方法,之前我們說(shuō)過(guò)enqueueNativeCall方法只是做方法申明,這個(gè)時(shí)候就是調(diào)用之前定義的方法,在我的這個(gè)工程里面可以debug到,fangshufeng這個(gè)方法的moduleID是1,methodID是1

然后debug到enqueueNativeCall中

可以看到最終傳給oc的queue是什么

如果只看CalendarManager的話當(dāng)你點(diǎn)擊Fading in的時(shí)候傳給oc的queue就是
[[1],[1],[['name','ggg']]]
然后通過(guò)nativeFlushQueueImmediate方法給到oc,會(huì)進(jìn)到下面的這個(gè)方法
- (void)handleBuffer:(NSArray *)buffer
{
NSArray *requestsArray = [RCTConvert NSArray:buffer];
NSArray<NSNumber *> *moduleIDs = [RCTConvert NSNumberArray:requestsArray[RCTBridgeFieldRequestModuleIDs]];
NSArray<NSNumber *> *methodIDs = [RCTConvert NSNumberArray:requestsArray[RCTBridgeFieldMethodIDs]];
NSArray<NSArray *> *paramsArrays = [RCTConvert NSArrayArray:requestsArray[RCTBridgeFieldParams]];
int64_t callID = -1;
if (requestsArray.count > 3) {
callID = [requestsArray[RCTBridgeFieldCallID] longLongValue];
}
// 1
NSMapTable *buckets = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
valueOptions:NSPointerFunctionsStrongMemory
capacity:_moduleDataByName.count];
//2
[moduleIDs enumerateObjectsUsingBlock:^(NSNumber *moduleID, NSUInteger i, __unused BOOL *stop) {
RCTModuleData *moduleData = self->_moduleDataByID[moduleID.integerValue];
dispatch_queue_t queue = moduleData.methodQueue;
NSMutableOrderedSet<NSNumber *> *set = [buckets objectForKey:queue];
if (!set) {
set = [NSMutableOrderedSet new];
[buckets setObject:set forKey:queue];
}
[set addObject:@(i)];
}];
//3
for (dispatch_queue_t queue in buckets) {
dispatch_block_t block = ^{
NSOrderedSet *calls = [buckets objectForKey:queue];
@autoreleasepool {
for (NSNumber *indexObj in calls) {
NSUInteger index = indexObj.unsignedIntegerValue;
[self callNativeModule:[moduleIDs[index] integerValue]
method:[methodIDs[index] integerValue]
params:paramsArrays[index]];
}
}
};
[self dispatchBlock:block queue:queue];
}
_flowID = callID;
}
在需要的解釋的地方做了個(gè)標(biāo)記
- 由于下面要用到
object->object所以這里使用map也就是NSMapTable來(lái)保存數(shù)據(jù); - 這個(gè)地方可能有點(diǎn)繞,如果對(duì)數(shù)據(jù)結(jié)構(gòu)不清楚的話比較難以理解,還是以
CalendarManager為例子,先來(lái)假設(shè)其他的數(shù)據(jù)如下所示:
假設(shè)js傳給oc的queue是這個(gè)樣子的
[
[38,19,41,41,1], // 最后一個(gè)1表示`CalendarManager`模塊
[19,1,4,3,1], // 最后一個(gè)1表示addEvent: location: 方法
[[...],[...],[...],[...],['name','ggg']] //['name','ggg']表示方面方法的入?yún)? ]
我們通過(guò)斷點(diǎn)來(lái)跟蹤一下數(shù)據(jù)
`modules`
<__NSArrayI 0x7b048e30>(
38,
19,
41,
41,
1
)
buckets
po buckets
NSMapTable {
[10] <OS_dispatch_queue: com.facebook.react.CalendarManagerQueue[0x7b652310]> -> {(
4
)}
[46] <OS_dispatch_queue: com.facebook.react.ShadowQueue[0x7b19cb90]> -> {(
0,
2,
3
)}
[47] <null> -> {(
1
)}
}
po methodIDs
<__NSArrayI 0x7b048da0>(
19,
1,
4,
3,
1
)
po paramsArrays
<__NSArrayI 0x7ee5f7c0>(
<__NSArray0 0x7a648bd0>(
)
,
<__NSSingleObjectArrayI 0x7b04c8d0>(
47
)
,
<__NSSingleObjectArrayI 0x7b04daf0>(
3
)
,
<__NSArrayI 0x7ee55570>(
4,
3,
{
frames = (
0,
"0.008888888888888889",
"0.03555555555555556",
"0.08000000000000002",
"0.1422222222222222",
"0.2222222222222223",
"0.3200000000000001",
"0.4355555555555557",
"0.5644444444444444",
"0.6799999999999999",
"0.7777777777777777",
"0.8577777777777778",
"0.9199999999999999",
"0.9644444444444443",
"0.9911111111111111",
1,
1
);
iterations = 1;
toValue = 1;
type = frames;
},
13
)
,
<__NSArrayI 0x7b2630e0>(
name,
ggg
)
做的事情就是:
遍歷模塊的數(shù)組 -> 在_moduleDataByID取出moduleData -> 以moduleData中的串行隊(duì)列作為key set作為value,而set中裝著的對(duì)應(yīng)的索引,由于module、method、Params都是索引值是一一對(duì)應(yīng)的,這個(gè)索引值即是當(dāng)前module在modules數(shù)組的索引值,也是method、Params的索引值。
- 上面已經(jīng)給出了bucket的具體內(nèi)容,循環(huán)后也就是調(diào)用下面的方法
[self callNativeModule:[moduleIDs[index] integerValue]
method:[methodIDs[index] integerValue]
params:paramsArrays[index]];
// 具體就是
[self callNativeModule:1 //可以知道這個(gè)1就是`CalendarManager`
method:1 // 這個(gè)1就是表示addEvent: location: 方法
params:['name','ggg'] //表示方面方法的入?yún)? ];
//具體上面怎么解析的下面會(huì)做分析
oc拿到數(shù)據(jù)得到模塊名稱(chēng)、方法名稱(chēng)、參數(shù),然后執(zhí)行