美文网首页
ReactNative IOS 运行原理之

ReactNative IOS 运行原理之

作者: 耗子_wo | 来源:发表于2021-01-31 01:50 被阅读0次

这一期我们来介绍下ReactNative IOS 运行原理之<OC TO JavaScript>,老规矩我们不做每个细节的介绍,我们只介绍调用的实现原理,首先介绍OC TO JavaScript端的调用过程:

1 . OC TO JavaScript 首先先上图大致流程:

我们不做过多的介绍只介绍说红框的部分,也就是OC TO JavaScript的主要调用部分

image.png
- (void)runApplication:(RCTBridge *)bridge
{
  NSString *moduleName = _moduleName ?: @"";
  NSDictionary *appParameters = @{
    @"rootTag" : _contentView.reactTag,
    @"initialProps" : _appProperties ?: @{},
  };
   // 调用RCTCxxBridge的enqueueJSCall:method:args:completion:方法
  [bridge enqueueJSCall:@"AppRegistry" method:@"runApplication" args:@[ moduleName, appParameters ] completion:NULL];
}

enqueueJSCall就是OC TO JavaScript的调用,大家先记住这个函数,首先我们从头开始,首先初始化的时候 JSIExecutor 中执行 flush 函数;flush 函数中会在首次时对 JS 和 native 进行绑定;在绑定之后 native 就可以调用 JS 函数,实现 OC TO JavaScript 之间的通信。绑定规则如下:

// 各种js方法向native的绑定
void JSIExecutor::bindBridge() {
  std::call_once(bindFlag_, [this] {
    // 通过js侧的__fbBatchedBridge获取对应的batchedBridge
    Value batchedBridgeValue =
        runtime_->global().getProperty(*runtime_, "__fbBatchedBridge");
    if (batchedBridgeValue.isUndefined()) {
      throw JSINativeException(
          "Could not get BatchedBridge, make sure your bundle is packaged correctly");
    }
// 把batchedBridge中的callFunctionReturnFlushedQueue 和 JSIExecutor对象的callFunctionReturnFlushedQueue_进行绑定
    Object batchedBridge = batchedBridgeValue.asObject(*runtime_);
    callFunctionReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "callFunctionReturnFlushedQueue");
  // 把batchedBridge中的invokeCallbackAndReturnFlushedQueue 和 JSIExecutor中的invokeCallbackAndReturnFlushedQueue_进行绑定;
    invokeCallbackAndReturnFlushedQueue_ = batchedBridge.getPropertyAsFunction(
        *runtime_, "invokeCallbackAndReturnFlushedQueue");
  // 把batchedBridge中的flushedQueue 和 JSIExecutor中的flushedQueue_进行绑定。
    flushedQueue_ =
        batchedBridge.getPropertyAsFunction(*runtime_, "flushedQueue");
  });
}

__fbBatchedBridge 其实就是一个全局的 MessageQueue对象
在BatchedBridge.js里面如下:

const MessageQueue = require('./MessageQueue');

const BatchedBridge: MessageQueue = new MessageQueue();

// Wire up the batched bridge on the global object so that we can call into it.
// Ideally, this would be the inverse relationship. I.e. the native environment
// provides this global directly with its script embedded. Then this module
// would export it. A possible fix would be to trim the dependencies in
// MessageQueue to its minimal features and embed that in the native runtime.

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

module.exports = BatchedBridge;

其中绑定的时候 batchedBridge.getPropertyAsFunction 是对于JavaScriptCore函数的封装而已,本质上还是调用了JavaScriptCore的API来实现的,这里是获取到了JS端的callFunctionReturnFlushedQueue函数指针保存到了callFunctionReturnFlushedQueue_里面准备进行OC TO JavaScript的调用

getPropertyAsFunctiond 方法的调用介绍如下:

Function Object::getPropertyAsFunction(Runtime& runtime, const char* name)
    const {
  Object obj = getPropertyAsObject(runtime, name);
  if (!obj.isFunction(runtime)) {
    throw JSError(
        runtime,
        std::string("getPropertyAsFunction: property '") + name + "' is " +
            kindToString(std::move(obj), &runtime) + ", expected a Function");
  };

  return std::move(obj).getFunction(runtime);
}



Object Object::getPropertyAsObject(Runtime& runtime, const char* name) const {
  Value v = getProperty(runtime, name);

  if (!v.isObject()) {
    throw JSError(
        runtime,
        std::string("getPropertyAsObject: property '") + name + "' is " +
            kindToString(v, &runtime) + ", expected an Object");
  }

  return v.getObject(runtime);
}
inline Value Object::getProperty(Runtime& runtime, const char* name) const {
  return getProperty(runtime, String::createFromAscii(runtime, name));
}
inline Value Object::getProperty(Runtime& runtime, const String& name) const {
  return runtime.getProperty(*this, name);
}

// 最终调用了这个JavaScriptCore API的这个函数 JSObjectGetProperty

jsi::Value JSCRuntime::getProperty(
    const jsi::Object &obj,
    const jsi::String &name) {
  JSObjectRef objRef = objectRef(obj);
  JSValueRef exc = nullptr;
  JSValueRef res = JSObjectGetProperty(ctx_, objRef, stringRef(name), &exc);
  checkException(exc);
  return createValue(res);
}

最终调用了这个JavaScriptCore API的这个函数 JSObjectGetProperty 去获取到JS代码的函数映射到OC代码里面,用来作为OC函数触发调用,关于JavaScriptCore的介绍我们打算再分一期来重点介绍这部分的内容

对于 bridge enqueueJSCall, RN会着 Instance->NativeToJsBridge->JSIExecutor 这个调用链调用了 JSIExecutor::callFunction 方法,方法内调用了 JSIExecutor 的 callFunctionReturnFlushedQueue_方法。
在 bindBridge 中callFunctionReturnFlushedQueue_是通过 runtime 的方式将 native 的callFunctionReturnFlushedQueue_指向了 js 中的callFunctionReturnFlushedQueue函数的。
native 将 moduleId、methodId、arguements 作为参数执行 JS 侧的 callFunctionReturnFlushedQueue 函数,函数会返回一个 queue;这个 queue 就是 JS 需要 native 侧执行的方法;最后 native 侧交给callNativeModules去执行对应的方法。
js 侧使用 callFunction 获取到指定的 module 和 method;使用 apply 执行对应方法。

// RCTxxBridge.mm

- (void)enqueueJSCall:(NSString *)module
               method:(NSString *)method
                 args:(NSArray *)args
           completion:(dispatch_block_t)completion{
    if (strongSelf->_reactInstance) {
        // 调用了Instance.callJSFunction
      strongSelf->_reactInstance->callJSFunction(
          [module UTF8String], [method UTF8String], convertIdToFollyDynamic(args ?: @[]));
    }
  }];
}
// Instance.cpp
void Instance::callJSFunction(
    std::string &&module,
    std::string &&method,
    folly::dynamic &&params) {
  callback_->incrementPendingJSCalls();
  // 调用NativeToJsBridge的callFunction
  nativeToJsBridge_->callFunction(
      std::move(module), std::move(method), std::move(params));
}
// NativeToJsBridge.cpp
void NativeToJsBridge::callFunction(
    std::string &&module,
    std::string &&method,
    folly::dynamic &&arguments) {
  runOnExecutorQueue([this,
                      module = std::move(module),
                      method = std::move(method),
                      arguments = std::move(arguments),
                      systraceCookie](JSExecutor *executor) {
    // 调用了JSIExecutor中的callFunction
    executor->callFunction(module, method, arguments);
  });
}
// JSIExecutor.cpp

void JSIExecutor::callFunction(
    const std::string &moduleId,
    const std::string &methodId,
    const folly::dynamic &arguments) {
// 如果还未将callFunctionReturnFlushedQueue_和js函数中的callFunctionReturnFlushedQueue函数进行绑定,那么首先进行绑定
  if (!callFunctionReturnFlushedQueue_) {
    bindBridge();
  }

  Value ret = Value::undefined();
  try {
    scopedTimeoutInvoker_(
        [&] {
        // 调用callFunctionReturnFlushedQueue_  传入JS moduleId、methodId、arguements 参数,JS侧会返回queue
          ret = callFunctionReturnFlushedQueue_->call(
              *runtime_,
              moduleId,
              methodId,
              valueFromDynamic(*runtime_, arguments));
        },
        std::move(errorProducer));
  } catch (...) {

  }
// 执行native modules
  callNativeModules(ret, true);
}
// MessageQueue.js

 callFunctionReturnFlushedQueue(
    module: string,
    method: string,
    args: any[],
  ): null | [Array<number>, Array<number>, Array<any>, number] {
    this.__guard(() => {
      this.__callFunction(module, method, args);
    });

    return this.flushedQueue();
  }

    __callFunction(module: string, method: string, args: any[]): void {
    this._lastFlush = Date.now();
    this._eventLoopStartTime = this._lastFlush;
    const moduleMethods = this.getCallableModule(module);
    moduleMethods[method].apply(moduleMethods, args);
  }

最终掉用到了 MessageQueue.js 里面的 callFunctionReturnFlushedQueue函数,里面使用JavaScript的apply语法来执行这个模块方法的调用
moduleMethods[method].apply(moduleMethods, args); 来执行JS函数

其中在AppRegistry.js里面对于这个JS模块进行了注册

//AppRegistry.js
BatchedBridge.registerCallableModule(‘AppRegistry’, AppRegistry);

MessageQueue.js 里面保存到了_lazyCallableModules数组里面

//MessageQueue.js
  registerCallableModule(name: string, module: Object) {
    this._lazyCallableModules[name] = () => module;
  }

__callFunction里面的 this.getCallableModule实际上就是按照名字取出了这个模块,然后进行调用

//MessageQueue.js
  getCallableModule(name: string): any | null {
    const getValue = this._lazyCallableModules[name];
    return getValue ? getValue() : null;
  }
  __callFunction(module: string, method: string, args: any[]): void {
    this._lastFlush = Date.now();
    this._eventLoopStartTime = this._lastFlush;

    //省略···

    const moduleMethods = this.getCallableModule(module);

    //省略···

    moduleMethods[method].apply(moduleMethods, args);
    Systrace.endEvent();
  }

这样就完成了OC TO JavaScript的调用,总结如果OC TO JavaScript端的调用的话总体来说源头就是enqueueJSCall方法:

// RCTxxBridge.mm

- (void)enqueueJSCall:(NSString *)module
               method:(NSString *)method
                 args:(NSArray *)args
           completion:(dispatch_block_t)

我们来总结一下调用流程:

  • native 执行完成 js 代码会发送一个RCTJavaScriptDidLoadNotification时间给 RCTRootView;

  • RCTRootView 接收时间后会使用batchedBridge->enqueueJSCall去执行AppRegistry.runApplication函数;启动 RN 页面。

  • 执行enqueueJSCall的过程会沿着Instance->NativeToJsBridge->JSIExecutor 这个调用链调用了 JSIExecutor::callFunction 方法,方法内调用了 JSIExecutor 的 callFunctionReturnFlushedQueue_方法。

  • callFunctionReturnFlushedQueue_由于已经和 JS 侧的 callFunctionReturnFlushedQueue 方法已经绑定,所以在执行此 js 函数时会执行 callFunction 方法,使用 js 的 apply 函数执行module.methodName 的调用。

值得一提的是官方推荐OC TO JavaScript使用RCTEventEmitter来实现,首先继承自他,实现如下:

// CalendarManager.h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface CalendarManager : RCTEventEmitter <RCTBridgeModule>

@end

然后通过 sendEventWithName 触发调用,其中这里面也是用到了enqueueJSCall:这个方法,大家可以细细回味一下,是不是很多东西都有共通性呢?

- (void)sendEventWithName:(NSString *)eventName body:(id)body
{
  RCTAssert(
      _bridge != nil || _invokeJS != nil,
      @"Error when sending event: %@ with body: %@. "
       "Bridge is not set. This is probably because you've "
       "explicitly synthesized the bridge in %@, even though it's inherited "
       "from RCTEventEmitter.",
      eventName,
      body,
      [self class]);

  if (RCT_DEBUG && ![[self supportedEvents] containsObject:eventName]) {
    RCTLogError(
        @"`%@` is not a supported event type for %@. Supported events are: `%@`",
        eventName,
        [self class],
        [[self supportedEvents] componentsJoinedByString:@"`, `"]);
  }
  if (_listenerCount > 0 && _bridge) {
    [_bridge enqueueJSCall:@"RCTDeviceEventEmitter"
                    method:@"emit"
                      args:body ? @[ eventName, body ] : @[ eventName ]
                completion:NULL];
  } else if (_listenerCount > 0 && _invokeJS) {
    _invokeJS(@"RCTDeviceEventEmitter", @"emit", body ? @[ eventName, body ] : @[ eventName ]);
  } else {
    RCTLogWarn(@"Sending `%@` with no listeners registered.", eventName);
  }
}

细心的你应该发现一个问题就是 callFunctionReturnFlushedQueue 执行的时候还执行了 return this.flushedQueue(); 去返回一个队列给Native端,这个队列有个什么作用呢,等下节我们讲到Javascript TO OC的时候为大家揭晓答案

好了OC TO JavaScript部分就到这里了,下一篇介绍JavaScript TO OC的调用原理···

相关文章

网友评论

      本文标题:ReactNative IOS 运行原理之

      本文链接:https://www.haomeiwen.com/subject/lzqxtltx.html