reactNative调用原生(iOS)方法的全过程(二)

1,954 阅读3分钟

上篇文章说到RN调用原生方法,最终会调用被挂载global上的nativeFlushQueueImmediate函数,但是留下了一个疑问,就是这个函数是怎么具体分发到每个具体的函数中的,

先来看下这个函数的实现,去除异常处理外就调用了callNativeModules(args[0], false);方法。

args[0]是一个二维数组,里面的元素分别是:

  1. 第一个元素是moduleID的集合,在iOS里有一个数组,存放着导出的模块,这个moduleID就是这个数组的下标

  2. 第二个元素是methodID的集合,表示着导出方法的下标

  3. 第三个元素RN调用iOS参数的集合 如果参数中包含了onSucc或者OnFail的回调函数的话,把callID << 1的值加到参数里当做OnFail的标识,把(callID << 1) | 1的值加到参数里当做onSucc的标识。

    这样onSucc和OnFail的标识就只有第一位不一样,OnFail第一位为0,onSucc第一位为1。

    原生端进行回调的时候,把这个值带上,进行 callID >>> 1就能得到原来的callID, 把这个值与1相&,如果为1,则调用onSucc,否则调用OnFail。

  4. 第四个元素是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:和所有的所有的参数类型,处理的逻辑见RCTModuleMethodprocessMethodSignature方法。

得到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中未执行的消息给返回回来,这个时候,原生端就会再去调用这些方法,如此反复。