iOS中的RN的通信核心 NativeToJsBridge 对JS环境的初始化

557 阅读4分钟

iOS中的RN的通信核心 NativeToJsBridge 对JS环境的初始化

1. NativeToJsBridge 的构造函数

react-cxx中初始化_reactInstance时, 初始化了NativeToJsBridge:

// Instance 的 初始化 bridge 方法
void Instance::initializeBridge(
    std::unique_ptr<InstanceCallback> callback, // std::make_unique<RCTInstanceCallback>(self),
    std::shared_ptr<JSExecutorFactory> jsef, //executorFactory
    std::shared_ptr<MessageQueueThread> jsQueue, //_jsMessageThread
    std::shared_ptr<ModuleRegistry> moduleRegistry) { 
        ...
    jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
        nativeToJsBridge_ = std::make_shared<NativeToJsBridge>(jsef.get(), moduleRegistry_,  jsQueue, callback_);

        nativeToJsBridge_->initializeRuntime();

        /**
         * After NativeToJsBridge is created, the jsi::Runtime should exist.
         * Also, the JS message queue thread exists. So, it's safe to
         * schedule all queued up js Calls.
         */
        jsCallInvoker_->setNativeToJsBridgeAndFlushCalls(nativeToJsBridge_);

        std::lock_guard<std::mutex> lock(m_syncMutex);
        m_syncReady = true;
        m_syncCV.notify_all();
    });
}

// NativeToJsBridge 的构造方法
NativeToJsBridge::NativeToJsBridge(
    JSExecutorFactory *jsExecutorFactory,
    std::shared_ptr<ModuleRegistry> registry,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<InstanceCallback> callback)
    : m_destroyed(std::make_shared<bool>(false)),
      m_delegate(std::make_shared<JsToNativeBridge>(registry, callback)),
      m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)),
      m_executorMessageQueueThread(std::move(jsQueue)),
      m_inspectable(m_executor->isInspectable()) {} 

NativeToJsBridge的构造方法必须在JSThread中执行!!! 以上代码中使用jsQueue->runOnQueueSync(std::function<> ...) 保证了!

在初始化完成以后, 主动调用NativeToJsBridge::initializeRuntime()方法主动初始化JS Context

先看一下NativeToJsBridge的几个成员变量:

class NativeToJsBridge {
  ...

  std::shared_ptr<JsToNativeBridge> m_delegate;
  std::unique_ptr<JSExecutor> m_executor; 
  std::shared_ptr<MessageQueueThread> m_executorMessageQueueThread;
}

在理解它之前, 需要先理解它的几个成员变量:

  1. std::unique_ptr<JSExecutor> m_executor;: JS的执行器, 或者说JS Context 执行环境!!!
  2. std::shared_ptr<JsToNativeBridge> m_delegate;: This class manages calls from JS to native code.
  3. std::shared_ptr<MessageQueueThread> m_executorMessageQueueThread;: 前面提到过的JSThread的封装类, 为了模拟dispatch_queue, 保证JS的执行逻辑都在该线程执行.

2. JS执行环境JSExcutor的创建

我们看到成员变量JSExcutor在创建的时候, 大概会涉及以下几个方法和对象:

// 1. 在 RCTCxxBridge.start 方法中
std::shared_ptr<JSExecutorFactory> executorFactory = std::make_shared<JSCExecutorFactory>(installBindings);

// 2. 在 NativeToJsBridge 构造函数中
jsExecutorFactory->createJSExecutor(m_delegate, jsQueue)

// 3. JSCExecutorFactory 的实现方法
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
    std::shared_ptr<ExecutorDelegate> delegate,
    std::shared_ptr<MessageQueueThread> __unused jsQueue) {
    return std::make_unique<JSIExecutor>(facebook::jsc::makeJSCRuntime(), delegate, JSIExecutor::defaultTimeoutInvoker, runtimeInstaller_);
}

// 4. JS 的 Runtime 实际是一个 JSCRuntime
std::unique_ptr<jsi::Runtime> makeJSCRuntime() {
  return std::make_unique<JSCRuntime>();
}

// 5. JSCRuntime 是 JavaScriptCore API的包装实现!!!
class JSCRuntime : public jsi::Runtime {
 public:
  JSCRuntime();
  JSCRuntime(JSGlobalContextRef ctx);
}

简单来说, JSExecutorFactory 会创建的JSExecutor, 底层是cxx类JSIExcutor的抽象, 具体落地实现的是JSCRuntime(iOS中包装 JavaScriptCore).

也就是说, 如果我们需要在native执行js语句, 原生方式使用JavaScriptCore API, RN完全封装到JSExecutor中了.

3. JSExcutor初始化js runtime, 注入关键函数!

前面提到, 在NativeToJsBridgeJSThread实例对象创建完成以后, 会初始化js runtime, 核心代码如下:

void NativeToJsBridge::initializeRuntime() {
  runOnExecutorQueue([](JSExecutor *executor) mutable {
      executor->initializeRuntime();
  });
}

void NativeToJsBridge::runOnExecutorQueue(std::function<void(JSExecutor *)> task) {
  if (*m_destroyed) {
      return;
  }
  std::shared_ptr<bool> isDestroyed = m_destroyed;

  m_executorMessageQueueThread->runOnQueue(
      [this, isDestroyed, task = std::move(task)] {
        if (*isDestroyed) {
          return;
        }
        // 能到这里, 说明 this.m_excutor 还没有释放, 因此直接进行调用
        // 另外再 task的执行过程中, m_excutor 一定会被保证不会被释放, 有以下几个原因
        // 1. excutor 只会在 它被 unregistred 以后才会 destory
        // 2. excutor unregistered 是在 JSThread 执行的!!!
        // 3. 在下面 task 执行之前, 我们刚刚判断了 *isDestroyed !!! 表示一定没有 destory..
        task(m_executor.get());
      });
}

先看一下runOnExecutorQueue方法, 实现比较清晰! 但是这里有一点需要注意的地方:

  1. 使用局部变量std::shared_ptr<bool> isDestroyed持有了m_destroyed.
  2. 在异步将std::function<void()> &&func加入到JSMessageQueue时, 构造的lambda会持有以下几个变量:
    1. this指针. 这里没有使用enable_from_this(), 是因为如果使用智能指针, 那么这个NativeToJsBridge不会主动释放. 这里这样实现的目标是, 如果NativeToJsBridge被释放了, JSMessageQueue才调度到这个task, 配合isDestory变量进行判断, 可以不用执行任务.
    2. isDestroyed单独用std::shared_ptr持有, 是为了防止 this 在task被调度时, 释放以后, 还能知道this是否有效.
    3. task = std::move(task), 是c++14的特性, 直接用移动构造给lambda形参.
    4. lambda任务是异步加入到JSMessageQueue中, 这个调度队列会在合适的时机调度.

接着executor->initializeRuntime(); 方法实际会调用JSIExecutor的具体方法:

// JSIExcutor 初始化 runtime 运行时...
void JSIExecutor::initializeRuntime() {
  runtime_->global().setProperty(
      *runtime_,
      "nativeModuleProxy",
      Object::createFromHostObject(
          *runtime_, std::make_shared<NativeModuleProxy>(nativeModules_)));

  runtime_->global().setProperty(
      *runtime_,
      "nativeFlushQueueImmediate",
      Function::createFromHostFunction(
          *runtime_,
          PropNameID::forAscii(*runtime_, "nativeFlushQueueImmediate"),
          1,
          [this](
              jsi::Runtime &,
              const jsi::Value &,
              const jsi::Value *args,
              size_t count) {
            if (count != 1) {
              throw std::invalid_argument(
                  "nativeFlushQueueImmediate arg count must be 1");
            }
            callNativeModules(args[0], false);
            return Value::undefined();
          }));

  runtime_->global().setProperty(
      *runtime_,
      "nativeCallSyncHook",
      Function::createFromHostFunction(
          *runtime_,
          PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
          1,
          [this](
              jsi::Runtime &,
              const jsi::Value &,
              const jsi::Value *args,
              size_t count) { return nativeCallSyncHook(args, count); }));

  if (runtimeInstaller_) {
    runtimeInstaller_(*runtime_);
  }
}

代码中主要做的是以下几个事情:

  1. std::shared_ptr<jsi::Runtime> runtime_; 引用的对象是 JSCRuntime. 通俗点理解, 就是 JS Context!!!
  2. runtime_->global()表示获取JS Context中的global对象.
  3. js global对象注入几个全局对象:
    1. global.nativeModuleProxy, 引用的是NativeModuleProxy, 它是 NativeModule的代理, 通过它能调用到 NativeModule
    2. global.nativeFlushQueueImmediate, 它是提供给js的全局方法, 代码中的lambda是它的函数体!!! 函数体中的 callNativeModules 从字面量就知道, 是js调用native module的方法!!!
    3. gloabl.nativeCallSyncHook, 它是提供给js的全局方法, 代码中的lambda是它的函数体!!!

以上, JS contenxt 就搞定了!!!

注意, JS Context 创建, 以及 JS runtime 注入函数, 都是在 JSThread 中执行的!!!