上篇文章说到RN调用原生方法,最终会调用被挂载global上的nativeFlushQueueImmediate
函数,但是留下了一个疑问,就是这个函数是怎么具体分发到每个具体的函数中的。
先来看下这个函数的实现,去除异常处理外就调用了callNativeModules(args[0], false);
方法。
args[0]
是一个二维数组,里面的元素分别是:
第一个元素是moduleID的集合,在iOS里有一个数组,存放着导出的模块,这个moduleID就是这个数组的下标
第二个元素是methodID的集合,表示着导出方法的下标
-
第三个元素RN调用iOS参数的集合
如果参数中包含了onSucc或者OnFail的回调函数的话,把callID << 1
的值加到参数里当做OnFail的标识,把(callID << 1) | 1
的值加到参数里当做onSucc的标识。这样onSucc和OnFail的标识就只有第一位不一样,OnFail第一位为0,onSucc第一位为1。
原生端进行回调的时候,把这个值带上,进行
callID >>> 1
就能得到原来的callID
, 把这个值与1相&,如果为1,则调用onSucc,否则调用OnFail。 第四个元素是callID,表示当前是第几次调用的原生方法,这里会把当前队列的最后的一次给传回来
上面用二维数组表示的意义是,为了不频繁与原生进行交互,在RN端有个判断逻辑是,如果当前的时间与上一次调用时间的时间差小于5毫秒,则把这次调用的信息存放到这个二维数组中,等到下一次调用的时候,把多次的调用信息传到iOS端
再回到callNativeModules
函数中,首先会把二维数组args[0]转成MethodCall
类型的数组,MethodCall
结构如下
struct MethodCall {
int moduleId; /// 模块ID,对应的模块数组中的下标
int methodId;/// 方法ID,对应的方法数组中的下标
folly::dynamic arguments;/// 参数
int callId;
};
接下来会通过moduleId得到对应的模块(RCTNativeModule),再通过methodId得到对应的方法(RCTModuleMethod),RCTModuleMethod获取到OC里的方法名字,如testPromise:(nullable NSString *)name resolve:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
,会对这个名字进行处理,得到selector testPromise: resolve: rejecter:
和所有的所有的参数类型,处理的逻辑见RCTModuleMethod
的processMethodSignature
方法。
得到selector之后,通过selector获取到NSMethodSignature
,在通过NSMethodSignature
生成NSInvocation
。
再把RN端传过来的参数传承NSInvocation
所对应类型的参数,然后执行NSInvocation
,执行NSInvocation
的target,是RCTNativeModule
中所保存的对应模块的实例,该实例是在初始化的时候创建的,并且之会创建一次,调用new
方法调用的。
如果调用了上面的resolve
或者rejecter
的回调,那么会调用RCTCxxBridge
的- (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args
,最终又会调用MessageQueue.js
中的invokeCallbackAndReturnFlushedQueue
方法,进行处理回调,会有2个参数,一个是左移之后的callID,一个是回调带回去的结果。
invokeCallbackAndReturnFlushedQueue
内部会通过callID找到对应的回调函数去执行,然后会把会把queue中未执行的消息给返回回来,这个时候,原生端就会再去调用这些方法,如此反复。