ReactNative源码分析-JS和Native的通信机制

2018/01/09 react-native, android

本文中我们将开始介绍Native和JS之间的通信,我们会分别以以下的主线进行介绍:
1.初始化,启动过程
2.Native到JS的调用路径
3.JS到Native的调用路径
4.Native到JS的Callback回调路径

期间我们会看到各个部分的执行线程,存储的数据结构,Native的方法是如何映射到JS中的等信息…
我们先来看一下相关模块的初始化。在上一篇介绍ReactActivity的启动过程中,我们已经看到了Native和Js交互的入口在CatalysInstanceImpl的实现类中,我们分别看下CatalysInstanceImpl的构造过程和c++部分的启动入口OnLoad.cpp

private CatalystInstanceImpl {
  ...
   mReactQueueConfiguration = ReactQueueConfigurationImpl.create(
        reactQueueConfigurationSpec,
        new NativeExceptionHandler());
  initializeBridge(
      new BridgeCallback(this),
      jsExecutor,
      mReactQueueConfiguration.getJSQueueThread(),
      mNativeModulesQueueThread,
      mNativeModuleRegistry.getJavaModules(this),
      mNativeModuleRegistry.getCxxModules());
  ...
}
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
  return initialize(vm, [] {
    gloginit::initialize();
    JSCJavaScriptExecutorHolder::registerNatives();
    ProxyJavaScriptExecutorHolder::registerNatives();
    CatalystInstanceImpl::registerNatives();
    CxxModuleWrapperBase::registerNatives();
    CxxModuleWrapper::registerNatives();
    JCxxCallbackImpl::registerNatives();
    NativeArray::registerNatives();
    ReadableNativeArray::registerNatives();
    WritableNativeArray::registerNatives();
    NativeMap::registerNatives();
    ReadableNativeMap::registerNatives();
    WritableNativeMap::registerNatives();
    ReadableNativeMapKeySetIterator::registerNatives();

    #ifdef WITH_INSPECTOR
    JInspector::registerNatives();
    #endif
  });
}

在c++部分的启动中,系统注册了一系列的jni方法提供和java层的基础交互。
在java层的初始化主要调用了initializeBridge 方法,initializeBridge又直接调用了jni的方法到c++部分,我们先来看下初始化的参数有哪些:
1.BridgeCallback 全局的回调函数,监听Bridge的一些行为
2.jsExecutor,实际上对应到c++中的JavaScriptHJSCJavaScriptExecutorHolder, 这个地方很难读,在java是一个JsExecutor实际上到了c++被映射成了JavaExecutorHolder,而JavaExecutorHolder用来生成一个JsExecutor的Factory,此处对应可以在OnLoad中发现蛛丝马迹 static constexpr auto kJavaDescriptor = “Lcom/facebook/react/bridge/JSCJavaScriptExecutor;”; 至于如何对应上的是facebook的Hybrid做的,没有深入研究。
3.mReactQueueConfiguration.getJSQueueThread(), 从名字上可以看到是JS的线程队列,我们先来看一下QueueThread的借口:

@DoNotStrip
public interface MessageQueueThread {
  /**
   * Runs the given Runnable on this Thread. It will be submitted to the end of the event queue even
   * if it is being submitted from the same queue Thread.
   */
  @DoNotStrip
  void runOnQueue(Runnable runnable);

  /**
   * Runs the given Callable on this Thread. It will be submitted to the end of the event queue even
   * if it is being submitted from the same queue Thread.
   */
  @DoNotStrip
  <T> Future<T> callOnQueue(final Callable<T> callable);

  /**
   * @return whether the current Thread is also the Thread associated with this MessageQueueThread.
   */
  @DoNotStrip
  boolean isOnThread();

  /**
   * Asserts {@link #isOnThread()}, throwing a {@link AssertionException} (NOT an
   * {@link AssertionError}) if the assertion fails.
   */
  @DoNotStrip
  void assertIsOnThread();

  /**
   * Asserts {@link #isOnThread()}, throwing a {@link AssertionException} (NOT an
   * {@link AssertionError}) if the assertion fails. The given message is appended to the error.
   */
  @DoNotStrip
  void assertIsOnThread(String message);

  /**
   * Quits this MessageQueueThread. If called from this MessageQueueThread, this will be the last
   * thing the thread runs. If called from a separate thread, this will block until the thread can
   * be quit and joined.
   */
  @DoNotStrip
  void quitSynchronous();
}

核心方法是runOnQueue和callOnQueue,分别传入Runnable 和 Callable,MessageQueueThread只有一个实现类,MessageQueueThreadImpl :


  private final String mName;
  private final Looper mLooper;
  private final MessageQueueThreadHandler mHandler;
  private final String mAssertionErrorMessage;

  @DoNotStrip
  @Override
  public void runOnQueue(Runnable runnable) {
    if (mIsFinished) {
      FLog.w(
          ReactConstants.TAG,
          "Tried to enqueue runnable on already finished thread: '" + getName() +
              "... dropping Runnable.");
    }
    mHandler.post(runnable);
  }

可以看到内部是通过handler来实现线程队列的.
回过头来我们再来看JsQueueThread的初始化, JsQueueThread的初始化是在ReactQueueConfigurationImpl.create方法中创建的:

  public static MessageQueueThreadImpl create(
      MessageQueueThreadSpec spec,
      QueueThreadExceptionHandler exceptionHandler) {
    switch (spec.getThreadType()) {
      case MAIN_UI:
        return createForMainThread(spec.getName(), exceptionHandler);
      case NEW_BACKGROUND:
        return startNewBackgroundThread(spec.getName(), spec.getStackSize(), exceptionHandler);
      default:
        throw new RuntimeException("Unknown thread type: " + spec.getThreadType());
    }
  }

根据传入的spec来决定创建在UI线程还是新的线程中,spec的设置可以在ReactInstanceManager的createReactContext中找到,调用的是ReactQueueConfigurationSpec.createDefault()方法,createDefault中默认的是使用BackThreadSpeck, 即启动后台线程。

4.nMativeModulesQueueThread NativeModules的执行线程 初始化过程同jsQueueThread也是默认启动的后台线程

5.mNativeModuleRegistry.getJavaModules Native注册的所有的JavaModules,构建过程可以在createReactContet中的processPackages中找到,我们就不具体分析了,感兴趣可以自行查阅。

6.mNativeModuleRegistry.getCxxModules() 同上。

下面我们进入c++的部分,查看下c++部分的具体实现,我们先来看react/jni/CatalystInstanceImpl.cpp中的initializeBridge方法:

void CatalystInstanceImpl::initializeBridge(
    jni::alias_ref<ReactCallback::javaobject> callback,
    // This executor is actually a factory holder.
    JavaScriptExecutorHolder* jseh,
    jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
    jni::alias_ref<JavaMessageQueueThread::javaobject> nativeModulesQueue,
    jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
    jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {
  // TODO mhorowitz: how to assert here?
  // Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
  moduleMessageQueue_ = std::make_shared<JMessageQueueThread>(nativeModulesQueue);

  // This used to be:
  //
  // Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> Bridge::Callback
  // --weak--> ReactCallback -> Java CatalystInstanceImpl
  //
  // Now the weak ref is a global ref.  So breaking the loop depends on
  // CatalystInstanceImpl#destroy() calling mHybridData.resetNative(), which
  // should cause all the C++ pointers to be cleaned up (except C++
  // CatalystInstanceImpl might be kept alive for a short time by running
  // callbacks). This also means that all native calls need to be pre-checked
  // to avoid NPE.

  // See the comment in callJSFunction.  Once js calls switch to strings, we
  // don't need jsModuleDescriptions any more, all the way up and down the
  // stack.

  moduleRegistry_ = std::make_shared<ModuleRegistry>(
    buildNativeModuleList(
       std::weak_ptr<Instance>(instance_),
       javaModules,
       cxxModules,
       moduleMessageQueue_));

  instance_->initializeBridge(
    folly::make_unique<JInstanceCallback>(
    callback,
    moduleMessageQueue_),
    jseh->getExecutorFactory(),
    folly::make_unique<JMessageQueueThread>(jsQueue),
    moduleRegistry_);
}

其中2个重要部分是 用传入的Modules创建了一个ModuleRegistry对象,调用了ReactCommon/cxxreact/Instance的initializeBridge方法
我们先来看ModuleRegistry对象,顾名思义可以理解这里是Module的仓库,存放了所有java层传入的Module,在ModuleRegistry的构造函数什么也没有做只是记录下了modules,其中提供了一个updateModuleNamesFromIndex, 会生成一个modulesByName_[]的数据结构用来根据名称去除index,即Module的Id。其中还提供了registerModules方法,可以支持在构造外的注入Module,具体的使用我们就不做分析了。

我们再来看Instance重的initializeBridge方法

void Instance::initializeBridge(
    std::unique_ptr<InstanceCallback> callback,
    std::shared_ptr<JSExecutorFactory> jsef,
    std::shared_ptr<MessageQueueThread> jsQueue,
    std::shared_ptr<ModuleRegistry> moduleRegistry) {
  callback_ = std::move(callback);
  moduleRegistry_ = std::move(moduleRegistry);

  jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
    nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
        jsef.get(), moduleRegistry_, jsQueue, callback_);

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

  CHECK(nativeToJsBridge_);
}

在方法中,系统调用jsQueue(之间在java层传入的JsQueuThread),的run方法,在js线程中继续之后的行为,在线程中系统创建了NativeToJsBridge对象(和js交互的Bridge)


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)) {}

在NativeToJsBridge的构造函数中,主要调用了jsExecutorFactory创建了一个JSExecutor, jsExecutorFactory就是java层传入的JsExecutor(所以说这里很坑,明明是JsExecutor对引导c++部分就变成了JsExecutorFactory),在查看OnLoad.cpp中的可以看到实际调用的是AndroidJSCFactory的makeAndroidJSCExecutorFactory, 是一个JSCExecutorFacory对象,JSCExecutorFacotry的实现在JSCExecutor.cpp文件中,实际上创建了一个JSCExecutor我们来看一下JSCExecutor的构造:

    JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
                             std::shared_ptr<MessageQueueThread> messageQueueThread,
                             const folly::dynamic& jscConfig) throw(JSException) :
    m_delegate(delegate),
    m_messageQueueThread(messageQueueThread),
    m_nativeModules(delegate ? delegate->getModuleRegistry() : nullptr),
    m_jscConfig(jscConfig) {
      initOnJSVMThread();

      {
        SystraceSection s("nativeModuleProxy object");
        installGlobalProxy(m_context, "nativeModuleProxy",
                           exceptionWrapMethod<&JSCExecutor::getNativeModule>());
      }
    }

可以看到这里有一个delegate delegate可以在NativeToJsBridge的构造函数中看到,是一个JsToNativeBridge对象。里面记录了一个ModuleRegistry。

我们总结一些初始化的工作:
1.创建了一个NativeToJsBridge对象
2.创建了一个JSCExecutor用来和JS交互
3.将本地的Module记录到ModuleRegistry中,已数组的index作为module的ID

初始化完了我们来看一下从JAVA获取JavaScriptModule开始到执行JS方法的过程,我们从ReactContext的getJSModule开始看起,它调用了CatalysInstanceImpl的getJSModule方法,在CatalysInstanceImpl中又调用到了JavaScriptModuleRegistry的getJavaScriptModule方法:

  public synchronized <T extends JavaScriptModule> T getJavaScriptModule(
      CatalystInstance instance,
      Class<T> moduleInterface) {
    JavaScriptModule module = mModuleInstances.get(moduleInterface);
    if (module != null) {
      return (T) module;
    }

    JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
        moduleInterface.getClassLoader(),
        new Class[]{moduleInterface},
        new JavaScriptModuleInvocationHandler(instance, moduleInterface));
    mModuleInstances.put(moduleInterface, interfaceProxy);
    return (T) interfaceProxy;
  }

内部实现使用了动态代理最终在调用方法时候走入到了JavaScriptModuleInvocationHandler中, 然后又触发到了CatalystInstanceImpl中的callFunction方法,然后进入了c++部分,调用Instance的callFunction方法,调用NativeToJsBridge的callFunction方法 :

void NativeToJsBridge::callFunction(
    std::string&& module,
    std::string&& method,
    folly::dynamic&& arguments) {
  int systraceCookie = -1;
  #ifdef WITH_FBSYSTRACE
  systraceCookie = m_systraceCookie++;
  FbSystraceAsyncFlow::begin(
      TRACE_TAG_REACT_CXX_BRIDGE,
      "JSCall",
      systraceCookie);
  #endif

  runOnExecutorQueue([module = std::move(module), method = std::move(method), arguments = std::move(arguments), systraceCookie]
    (JSExecutor* executor) {
      #ifdef WITH_FBSYSTRACE
      FbSystraceAsyncFlow::end(
          TRACE_TAG_REACT_CXX_BRIDGE,
          "JSCall",
          systraceCookie);
      SystraceSection s("NativeToJsBridge::callFunction", "module", module, "method", method);
      #endif
      // This is safe because we are running on the executor's thread: it won't
      // destruct until after it's been unregistered (which we check above) and
      // that will happen on this thread
      executor->callFunction(module, method, arguments);
    });
}

然后调用了runOnExecutorQueue(实际上是Java层创建的MessageQueueThread , 即js线程),将具体的执行放入js线程队列,在线程中调用JSCExecutor的callFunction方法执行具体的方法,在JSExecutor中,JSExecutor通过global设置的属性来获取js中的方法以实现和js的交互,JSExecutor的callFunction如下:

 void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
      SystraceSection s("JSCExecutor::callFunction");
      // This weird pattern is because Value is not default constructible.
      // The lambda is inlined, so there's no overhead.
      auto result = [&] {
        JSContextLock lock(m_context);
        try {
          if (!m_callFunctionReturnResultAndFlushedQueueJS) {
            bindBridge();
          }
          return m_callFunctionReturnFlushedQueueJS->callAsFunction({
            Value(m_context, String::createExpectingAscii(m_context, moduleId)),
            Value(m_context, String::createExpectingAscii(m_context, methodId)),
            Value::fromDynamic(m_context, std::move(arguments))
          });
        } catch (...) {
          std::throw_with_nested(
                                 std::runtime_error("Error calling " + moduleId + "." + methodId));
        }
      }();
      callNativeModules(std::move(result));
    }

先判断js中的方法是否获取到,没有的话就调用bindBridge方法,bindBridge中从global中获取js的方法。 bind结束后调用了m_callFunctionReturnFlushedQueueJs方法,即JS中的callFunctionReturnFlushedQueue方法, js中的对应方法主要调用了2个方callFuncion和flushedQueue,我们先来看一下callFunction方法,对应的方法存在于BatchedBridge下的MessageQueue.js文件中:


  __callFunction(module: string, method: string, args: any[]): any {
    this._lastFlush = new Date().getTime();
    this._eventLoopStartTime = this._lastFlush;
    Systrace.beginEvent(`${module}.${method}()`);
    if (this.__spy) {
      this.__spy({type: TO_JS, module, method, args});
    }
    const moduleMethods = this.getCallableModule(module);
    invariant(
      !!moduleMethods,
      'Module %s is not a registered callable module (calling %s)',
      module,
      method,
    );
    invariant(
      !!moduleMethods[method],
      'Method %s does not exist on module %s',
      method,
      module,
    );
    const result = moduleMethods[method].apply(moduleMethods, args);
    Systrace.endEvent();
    return result;
  }

getCallableModule方法直接从_lazyCallableModules中取出对应的Module,而在registerCallableModule的时候会将module记录在_lazyCallableModules中,它记录了所有的jsModule。获取到Module中的所有方法后通过方法名获取到具体的方法然后调用apply开始执行方法。

我们可以用一张序列图来表示执行的过程:

我们再来看一下从jS到Native层的调用,我们从NativeModules.js看起

let NativeModules : {[moduleName: string]: Object} = {};
if (global.nativeModuleProxy) {
  NativeModules = global.nativeModuleProxy;
} else {
  const bridgeConfig = global.__fbBatchedBridgeConfig;
  invariant(bridgeConfig, '__fbBatchedBridgeConfig is not set, cannot invoke native modules');

  const defineLazyObjectProperty = require('defineLazyObjectProperty');
  (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);
    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)
      });
    }
  });
}

可以看到js中对所有的nativeModule都调用了genModule方法生成Module对象,然后通过genMethod生成方法,当方法调用时 实际上调用了BatchedBridge.enqueueNativeCall方法:

const BatchedBridge = new MessageQueue(
  // $FlowFixMe
  typeof __fbUninstallRNGlobalErrorHandler !== 'undefined' &&
    __fbUninstallRNGlobalErrorHandler === true, // eslint-disable-line no-undef
);

BatchedBridge实际上是MessageQueue对象,在MessageQueue中有几个重要的数据结构:

  _queue: [number[], number[], any[], number];
  _successCallbacks: (?Function)[];
  _failureCallbacks: (?Function)[];
  _callID: number;

其中queue记录了所有还没有被执行的方法,4个参数分别表示 moduleId, methodId, callId, 其中mouduleId 和 methodid实际上都是c++中存储的Module的下标。我们接着看enqueueNativeCall方法:

this._callId++;
    if (onFail || onSucc) {
      onFail && params.push(this._callID << 1);
      onSucc && params.push((this._callID << 1) | 1);
      this._successCallbacks[this._callID] = onSucc;
      this._failureCallbacks[this._callID] = onFail;
    }

         global.nativeFlushQueueImmediate(queue);

核心代码入上图所示,如果从在回调的onFail和onSucc方法则分别加入到successCallbacks和failureCallbacks中,index为callId, callId是一个自增的计数器,然后如果是onFail方法则在params中加入_callId左移一位,onSucc则左移一位末尾补1,即使用_callId的最后一位用来标识是否存在回调方法。
gloal.nativeFlushQueueImmediate方法实际绑定JSCExecutor中的对应方法:

 void JSCExecutor::flushQueueImmediate(Value&& queue) {
      auto queueStr = queue.toJSONString();
      m_delegate->callNativeModules(*this, folly::parseJson(queueStr), false);
    }

然后调用m_delegate的callNativeModules方法,之前我们分析过这里的m_delegate实际上是JSToNativeBridge对象,我们来看其中的方法:

 void callNativeModules(
      JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {

    CHECK(m_registry || calls.empty()) <<
      "native module calls cannot be completed with no native modules";
    m_batchHadNativeModuleCalls = m_batchHadNativeModuleCalls || !calls.empty();

    // An exception anywhere in here stops processing of the batch.  This
    // was the behavior of the Android bridge, and since exception handling
    // terminates the whole bridge, there's not much point in continuing.
    for (auto& call : parseMethodCalls(std::move(calls))) {
      m_registry->callNativeMethod(call.moduleId, call.methodId, std::move(call.arguments), call.callId);
    }
    if (isEndOfBatch) {
      // onBatchComplete will be called on the native (module) queue, but
      // decrementPendingJSCalls will be called sync. Be aware that the bridge may still
      // be processing native calls when the birdge idle signaler fires.
      if (m_batchHadNativeModuleCalls) {
        m_callback->onBatchComplete();
        m_batchHadNativeModuleCalls = false;
      }
      m_callback->decrementPendingJSCalls();
    }
  }

这里将js传入的queue中的数据全部取出来然后依次调用registry的callNativeMethod方法,registry中又根据moduleID去除具体的module然后调用invoke方法,可以看NativeModule方法中的invoke方法,NativeModule的实现类是JavaModuleWrapper,我们来看具体的方法实现


void JavaNativeModule::invoke(unsigned int reactMethodId, folly::dynamic&& params, int callId) {
  messageQueueThread_->runOnQueue([this, reactMethodId, params=std::move(params), callId] {
    static auto invokeMethod = wrapper_->getClass()->getMethod<void(jint, ReadableNativeArray::javaobject)>("invoke");
    #ifdef WITH_FBSYSTRACE
    if (callId != -1) {
      fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId);
    }
    #endif
    invokeMethod(
      wrapper_,
      static_cast<jint>(reactMethodId),
      ReadableNativeArray::newObjectCxxArgs(std::move(params)).get());
  });
}

使用了messageQueueThread(实际上是java传入的NativeModule的执行线程队列控制),然后走到了java中对应的JavaMethodWrapper中,在其将数据进行转换并使用反射调用了具体的方法。

如果带有callId的参数将被转换成Callback对象,当调用Callback对象的invoke方法时会走到CatalystyInstanceImpl中的invokeJsCallback方法最终通过一系列调用(同callFunction)最终走到js线程中,并执行JSCExecutor的InvockCallback方法,实际上又走入了JS的invokeCallbackAndReturnFlushQueue方法中,我们直接来看hs中的方法,js中又走了2步invokeCallback和flushQueue ,flushQueue我们就不在讲述,直接来看invokeCallback

  __invokeCallback(cbID: number, args: any[]) {
    this._lastFlush = new Date().getTime();
    this._eventLoopStartTime = this._lastFlush;

    // The rightmost bit of cbID indicates fail (0) or success (1), the other bits are the callID shifted left.
    // eslint-disable-next-line no-bitwise
    const callID = cbID >>> 1;
    // eslint-disable-next-line no-bitwise
    const isSuccess = cbID & 1;
    const callback = isSuccess
      ? this._successCallbacks[callID]
      : this._failureCallbacks[callID];

    if (__DEV__) {
      const debug = this._debugInfo[callID];
      const module = debug && this._remoteModuleTable[debug[0]];
      const method = debug && this._remoteMethodTable[debug[0]][debug[1]];
      if (!callback) {
        let errorMessage = `Callback with id ${cbID}: ${module}.${method}() not found`;
        if (method) {
          errorMessage =
            `The callback ${method}() exists in module ${module}, ` +
            'but only one callback may be registered to a function in a native module.';
        }
        invariant(callback, errorMessage);
      }
      const profileName = debug
        ? '<callback for ' + module + '.' + method + '>'
        : cbID;
      if (callback && this.__spy) {
        this.__spy({type: TO_JS, module: null, method: profileName, args});
      }
      Systrace.beginEvent(
        `MessageQueue.invokeCallback(${profileName}, ${stringifySafe(args)})`,
      );
    }

    if (!callback) {
      return;
    }

    this._successCallbacks[callID] = this._failureCallbacks[callID] = null;
    callback(...args);

    if (__DEV__) {
      Systrace.endEvent();
    }
  }

可以看到其中根据callId的最后一位确定调用的是onFail还是onSucc然后从对应的队列中执行找到对应的js方法然后执行。
JsToNative的调用过程如下图:

我对本文中涉及到的类画了一个简单的类图可以作为参照:


欢迎关注我的微信公众号

璐豪笔记

CoderHouse

Search

    Post Directory