Skip to content

RN 原理篇:通信原理

一 前言

在前两章节中,讲到了 RN 在 JS 运行时的原理,其中讲到 JS 运行时会形成虚拟 DOM ,然后通过桥通信向 Native 发送渲染指令,最终由 Native 渲染视图。在整个 RN 应用中,通信是非常重要的一部分。本章节将围绕 RN 的通信展开,研究一下 RN 通信背后的运转本质。

在 RN 的通信模块,可以从三个角度去分析:

  • 首先就是 JS (逻辑层)和 Native (渲染层)是怎么建立起关联的。
  • JS 向 Native 通信,会发生什么?
  • Native 向 JS 通信,会发生什么?

在前面的章节讲到,Native 和 JS 的通信,主要靠 C++ 中间层,在 RN 新的架构中,增加了 JSI, 有了 JSI 可以让 JS 和 C++ 相互感知,让通信变得更加容易。那么这一切是怎么串联起来的呢? 本文会以安卓为切入点,来共同分析一下核心流程:

二 RN 核心组成部分及初始化流程

在正式介绍之前,我们看一下 RN 的核心构成。还是按照渲染层 Native, 中间层 C++ 层和逻辑层 JS 来分析。

2.1 Native层核心

以安卓为例子,来看一下核心模块:

ReactRootView 视图层管理类:

这是 RN 中的一个类,继承了 FrameLayout,可以作为 Activity 或者 Flagment 的容器。如下所示:

java
public class ReactRootView extends FrameLayout implements RootView, ReactRoot {
    //...
}

这个类可以理解为承载 RN 应用的视图层,其中存在 startReactApplication 方法,可以用来启动 RN 应用,用简化的代码看一下核心流程。

java
public void startReactApplication(
      ReactInstanceManager reactInstanceManager, // React JS 上下文
      String moduleName,
      @Nullable Bundle initialProperties,
      @Nullable String initialUITemplate) {
           /* 创建 React JS 上下文,reactInstanceManager 关联起来。 */
           mReactInstanceManager.createReactContextInBackground();
           /* 将视图层 ReactRootView 和 reactInstanceManager 关联起来 */
           Assertions.assertNotNull(mReactInstanceManager).attachRootView(this);
      }

这个方法做的事情很简单,先用 mReactInstanceManager 上的 createReactContextInBackground 的方法,创建一个 RN 的 JS 的上下文,这个方法内部会通过 handleReloadJS 来加载 JS bundle (我们写在 RN 应用中的 JS 文件最终会被打包成一个 JS Bundle 文件)。

ReactInstanceManager React JS 实例管理类: 这个类用来管理 RN JS 上下文,初始化通信类,解析 ReactPackage 生成 NativeModule 注册表。其细节如下

回到 createReactContextInBackground 方法上来,会通过 Java 中的 Thread 类创建一个运行 JS 的 Java 线程,并通过 createReactContext 创建 React 上下文。

java
new Thread(
    null,
    new Runnable() {
        public void run() {
             /* 创建 JS 运行上下文  */
             reactApplicationContext =createReactContext(initParams.getJsExecutorFactory().create(),initParams.getJsBundleLoader());
        }
    }
)

来看一下 createReactContext 这个方法:

java
 private ReactApplicationContext createReactContext(
      JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
    /* 初始化 Native Module 注册表 */      
    NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
    /* 生成 catalystInstanceBuilder 实例  */
      catalystInstance = catalystInstanceBuilder.build(); 
    /* 将 catalystInstance 赋值到 reactContext 中 */
    reactContext.initializeWithInstance(catalystInstance); 
    /* 执行对应的 bundle */
    catalystInstance.runJSBundle();
    return reactContext;
  }

在这个方法中,有很多核心逻辑,会初始化生成 Native Module 注册表信息,然后生成 catalystInstance 实例。 将 catalystInstance 赋值到 reactContext 中 ,接下来就是执行对应的 JS bundle。这个实例是做什么的呢?接着往下看:

catalystInstance通信管理类: 这个类负责,Native 渲染层,C++ 中间层,和 JS 逻辑层的通信关系,负责调用 Native 的方法和执行 Native 回调函数,把 Native 的信息传递给 C++ 层,在由 C++ 层传递给 JS 层。也可以批量处理 JS 传递给 C++ 层再转到 Native 层的事件回调。

java
private CatalystInstanceImpl() {
    mHybridData = initHybrid();
    mReactQueueConfiguration =
        ReactQueueConfigurationImpl.create(
            reactQueueConfigurationSpec, new NativeExceptionHandler());
    /* 初始化桥 */        
    initializeBridge(
        new BridgeCallback(this),
        ...
    );
}

初始化流程:

2.2 C++ 核心构成

CatalystInstanceImpl 不只是是 Native (安卓侧)存在,在 C++ 也有通信管理类的实现,用于和 JS 线程(逻辑层)完成通信。将 NativeModules 传递到 C++ 层, 形成 Native 注册表。最终会调用 instance 实例会完成桥的初始化工作.

c++
void CatalystInstanceImpl::initializeBridge(...) {
  
  /* 线程赋值 */
  moduleMessageQueue_ =
      std::make_shared<JMessageQueueThread>(nativeModulesQueue);

  // 通信流程:Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> 回调函数
  // JS to Native 通信流程: React 回调函数 -> Java CatalystInstanceImpl
  /* 形成 Native module 注册表 */
  moduleRegistry_ = std::make_shared<ModuleRegistry>(buildNativeModuleList(...));
  /* 调用 instace 下面的 initializeBridge 方法,完成桥的注册 */
  instance_->initializeBridge(...);
}

CatalystInstanceImpl 中调用初始化桥,形成 Native module 注册表,然后调用 instace 下面的 initializeBridge 方法,完成桥的注册。

在 instance 的 initializeBridge 方法中,会完成 NativeToJsBridge 和 JsToNative 的初始化, 负责着 Native 和 Js 线程的双向通信。并且会通过 JSIExecutor 初始化执行 JS 代码。

通信流程:

  • Native 向 JS 通信: Java CatalystInstanceImpl -> C++ CatalystInstanceImpl -> Bridge -> 回调函数。
  • JS 向 Native 通信流程: React 回调函数 -> Java CatalystInstanceImpl。

整体流程如下:

2.3 JS 侧核心组成

在 JS 层,和 C++ 层桥接的部分就是 MessageQueue 类。

js
class MessageQueue{
    /* 保存所有的事件通信模块 */
    _lazyCallableModules = {}
    /* 存放回调函数 */
    _successCallbacks = new Map()
    /* 注册事件模块 */
    registerCallableModule(name,module){
        this._lazyCallableModules[name] = () => module;
    }
    /* 由 c++ 侧触发事件模块 */
    callFunctionReturnFlushedQueue(){}
    /* 调用 Native 方法 */
    enqueueNativeCall(){}
    /* 执行回调函数 */
    processCallbacks(){}
    /* 由 Native 触发,执行对应的回调函数 */
    __invokeCallback(){}
}

在 MessageQueue 上,存放着 Native 层 <-> JS 层双向通信的接口。比如 RN AppRegistry 完成初始化之后,需要 Native 主动调用 AppRegistry 上的 runApplication 方法,这个时候就需要通过 registerCallableModule 把 AppRegistry 注册到 _lazyCallableModules 中,来看一下 RN 中是怎么做的?

事件通信场景一:注册 AppRegistry

js
BatchedBridge.registerCallableModule('AppRegistry', AppRegistry);

在 AppRegistry.js 文件中,将 AppRegistry 注册到 MessageQueue。

事件通信场景二:注册 RCTEventEmitter

另外一个场景,就是发生事件交互,比如点击事件,那么需要 Native 向 JS 通信,这个时候需要向 RCTEventEmitter 注册事件。如下所示:

js
const RCTEventEmitter = {
  register(eventEmitter: any) {
    BatchedBridge.registerCallableModule('RCTEventEmitter', eventEmitter);
  },
};

三个模块的关系如下

三 RN 事件注册

如上讲到了整个通信核心组成部分,以及 JS 环境初始化流程。接下来研究一下整个事件是怎么注册的。注册分为两种:

JS Module 注册表 第一种就是 JS 注册,比如运行时章节中,讲到 AppRegistry 用于注册 RN 应用,但是对于 AppRegistry 中 registerComponent (注册 React 运行时应用) runApplication (运行 React 运行时应用) 的执行时机还是由 Native 侧控制的。

这个时候就需要一个 JS 注册表 JavaScriptModule 可以通过 Native 调用。一旦JavaScriptModule 创建完毕,其内部将维护一个HashMap,这个 HashMap 会被CatalystInstanceImpl 实例所持有,以便在后续 Java 调用 JS 时使用。

所有的 JS 模块都将继承自 JavaScriptModule 接口,在 JS 层有对应的模块方法,然后 JavaScriptModule 会形成一个接口代理类,通过 C++ 传递给 JS 层。接下来可以通过调用代理类的方法,实现对 JS 层方法的调用。比如 runApplication 等。

Native Moudle 注册表

JS Module 是为了 Native 调用 JS 文件,比如运行 RN 应用,那么 Native Moudle 是为了 JS 调用 Native, 比如调用 Native 的方法等。

比如在 RN 中写了一个 Native 模块 ReactNativeCommonModule, 那么在 RN 应用中,比如调用 Native 的方法有两种方式,第一种方式是:

NativeModules.NativeCommonModule.methodXXX() ,NativeCommonModule 为 moduleName (module 名称),methodXXX 为 methodId 。

另一种是异步调用方式:

NativeModules.NativeCommonModule.callNativeMethod('methodXXX', (result) => { console.log(result); });

其中 NativeCommonModule 为 moduleName (module 名称),methodXXX 为 methodId ,callbackId 为回调函数的 id 。

如下:

js
// 引用
import { NativeModules } from 'react-native'
const NativeCommonModule =  NativeModules?.ReactNativeCommonModule

// 调用对应方法
NativeCommonModule.methodXXX()
// 异步事件调用
NativeCommonModule.callNativeMethod('methodXXX',()=>{
    // 异步方法
})

那么背后的运转机制是什么呢?在 Native 中,通过 NativeModulesRegistry 注册 NativeModule , 在安卓 CatalystInstanceImpl 初始化桥的时候,会将 NativeModule 传递到 JSI C++ 层。我们再看一下初始化桥的方法。

java
initializeBridge(
        new BridgeCallback(this), //回调函数
        jsExecutor, // JS 执行器,用于执行 JS 
        mReactQueueConfiguration.getJSQueueThread(), // JS 线程
        mNativeModulesQueueThread, // NativeModule 线程
        mNativeModuleRegistry.getJavaModules(this),
        mNativeModuleRegistry.getCxxModules()
    );

如上将 NativeModule 传递到 C++ 层。

在 C++ 中,通过 ModuleHolder 保存 NativeModule 的模块名称和模块信息。在 C++ 中的 CatalystInstanceImpl 初始化桥,通过 buildNativeModuleList 构建出 NativeModule 数组,映射成 moduleRegistry_ 对象。 最后把这个对象传递给 instance 。

c++
void JSIExecutor::initializeRuntime() {
  SystraceSection s("JSIExecutor::initializeRuntime");
  /* 向 JS 运行时 global 对象上,挂载 nativeModuleProxy 对象 */
  runtime_->global().setProperty(
      *runtime_,
      "nativeModuleProxy",
      Object::createFromHostObject(
          *runtime_, std::make_shared<NativeModuleProxy>(nativeModules_)));

最后处理成 nativeModuleProxy 对象,挂载在 global 对象的 nativeModuleProxy 上。这样就可以在 JS 运行时中,就可以在直接获取 NativeModules 对象了。

当调用 NativeModules.ReactNativeCommonModule 模块的时候,本质上访问的是 nativeModuleProxy 的 get 方法。在之前讲 JSI 的时候,讲到了对象的相互感知, 本质上就是这个的实现。

c++
Value get(Runtime &rt, const PropNameID &name) override {
    if (name.utf8(rt) == "name") {
      return jsi::String::createFromAscii(rt, "NativeModules");
    }
    auto nativeModules = weakNativeModules_.lock();
    if (!nativeModules) {
      return nullptr;
    }

    return nativeModules->getModule(rt, name);
  }

如上在调用 nativeModules.getModule 方法的时候,会创建模块信息,进入缓存,下次可以直接从缓存中读取。在 C++ 注册表中获取模块信息。

最后把模块信息和 moduleId 传入到 JS 侧,JS 侧完成模块的生成。

接下来,调用模块下方法 例如 NativeCommonModule.methodXXX() ,完成方法的调用。

四 Native 向 JS 通信

Native 发起事件

接下来看一下 JS 向 Native 通信流程。举一个例子,当 RN 完成环境初始化之后,想要通过 runApplication 运行 RN 应用的时候,会经历哪里流程。

在视图层管理类 ReactRootView 中,本质上是调用 runApplication 运行 RN 应用 ,通过 CatalystInstance 获取对应的 JSModule 类,启动应用。

java
public void runApplication() {
    catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
}

Native 向 JS 通信主要依靠的是 JS Module 注册表。在执行 JSModule 方法时,我们首先通过 JSModule 注册表寻找对应的实例。如果没有找到,我们会初始化并缓存一个新的实例;如果找到了,我们就直接使用它。

java
public <T extends JavaScriptModule> T getJSModule(Class<T> jsInterface) {
    return mJSModuleRegistry.getJavaScriptModule(this, jsInterface);
}

在获取 JSModule 的时候,使用了懒加载和动态代理。

  • 懒加载,使用时才初始化,初始化后就缓存;避免了API的重复初始化。
  • 动态代理,在运行时,动态的创建代理类;这里缓存的也是对应class接口和代理类实例的缓存。

接下来就获取到了对应的 JSModule—AppRegistry,在 catalystInstance 获取 AppRegistry JSModule 并执行 runApplication 方法。

在 Native 中 AppRegistry 类继承了 JS Module 类,AppRegistry 实例管理着运行 RN 应用的入口。

js
public interface AppRegistry extends JavaScriptModule { 
   void runApplication(String appKey, WritableMap appParameters);
}

这个过程中,使用了动态代理。当我们通过具体实例调用对应的方法时,所有的调用都会统一转发到 InvocationHandler 实现类的 invoke 方法上。

在 invoke 方法中,本质上是调用 callFunction 。

js
 public @Nullable Object invoke()
        throws Throwable {
      mCatalystInstance.callFunction(getJSModuleName(), method.getName(), jsArgs);
      return null;
    }

callFunction 中调用 jsiCallFunction 进入到 Native 流程中。

在调用 jsiCallFunction 过程中,如果是初始化 RN 应用,那么传入的是 moduleID (AppRegistry)和 methodID (runApplication)。

C++事件通信

接下来 C++ 事件通信中,在 C++ 的 catalystInstance 中会调用 instance 下面 nativeToJsBridge 模块下callJSFunction 方法,如下:

c++
void Instance::callJSFunction(模块, 方法, 参数) {
  nativeToJsBridge_->callFunction(模块, 方法, 参数);
}

在 nativeToJsBridge 中会调用 JS 执行类 JSIExecutor 中的 callFunction 方法。如下所示:

c++
void NativeToJsBridge::callFunction(模块, 方法, 参数){
      executor->callFunction(模块名称, 方法名称, 参数);
}

接下来看一下 JSIExecutor 的 callFunction 方法。

c++
void JSIExecutor::callFunction(模块, 方法, 参数){
     try {
    scopedTimeoutInvoker_(
        [&] {
          ret = callFunctionReturnFlushedQueue_->call(
              *runtime_,
              moduleId,
              methodId,
              valueFromDynamic(*runtime_, arguments));
        },
        std::move(errorProducer));
  } catch (...) {
    // 处理异常
  }

  callNativeModules(ret, true);
}

在这个方法中,最后会调用 JS 中 MessageQueue 的 callFunctionReturnFlushedQueue。看一下这个方法的实现。

js
 class MessageQueue{
    /* c++ 触发 */
    callFunctionReturnFlushedQueue(模块名,方法名,参数){
        this.__callFunction(模块名,方法名,参数);
    }
    /* 调用方法 */
    __callFunction(模块名,方法名,参数){
        const moduleMethods = this.getCallableModule(模块); // AppRegistry
        moduleMethods[方法名].apply(模块, 参数); // AppRegistry.runApplication
    }
 }

最终 C++ 触发 callFunctionReturnFlushedQueue 会调用 __callFunction 然后向如上的 _lazyCallableModules 找到 AppRegistry,并调用方法 runApplication, 这样 RN 应用就可以正常运转了。

整体流程图如下:

五 JS 通信向 Native 通信

对于 JS 向 Native 的通信,分为同步调用异步调用两种方式,正常情况下是异步调用方式,同步方式会阻塞后续代码执行。

调用方法:

如上当调用 NativeModules.NativeCommonModule.methodXXX() 的时候,会发生了什么?

当调用 NativeModules 的时候,本质上就是 global.nativeModuleProxy ,nativeModuleProxy 在 C++ 层被赋值并挂载到 global 下,这使得 JS 侧可以直接进行调用。NativeModuleProxy 中存储的是 NativeModules注册表的信息。通过 JSCRuntime.cpp 的 setPropertyValue 方法,我们将原生对象 HostObjectProxy 与之关联。

当调用 NativeModules 下面的具体桥的时候,会触发 JSCRuntime.cpp 的 getProperty 方法,最终调用JSIExecutor.cpp 的 get 方法,最终调用 C++ nativeModules 下的 getModule 方法。

c++
Value JSINativeModules::getModule(Runtime &rt, const PropNameID &name) {
  if (!m_moduleRegistry) {
    return nullptr;
  }

  std::string moduleName = name.utf8(rt);
  
  /* 如果存在那么直接返回 it -> second, 里面包含 */
  const auto it = m_objects.find(moduleName);
  if (it != m_objects.end()) {
    return Value(rt, it->second);
  }
  /* 如果不存在,那么会创建一个 module */
  auto module = createModule(rt, moduleName);
    
  /* 保存到 m_objects 上 */  
  auto result =
      m_objects.emplace(std::move(moduleName), std::move(*module)).first;

  Value ret = Value(rt, result->second);
  return ret;
}

getModule 中会判断缓存中有没有对应的模块,如果有直接返回,如果没有需要通过 createModule 创建一个模块,并保存到 m_object 模块上,然后将结果返回。

如果第一次调用 NativeModules.NativeCommonModule , 那么返回值就是 createModule 返回的内容,createModule 到底做了些什呢?

在 createModule 中会从 moduleRegistry 中找到配置信息,然后通过 JS 侧 genMethod 包装 module 方法。比如调用 NativeCommonModule 下面的 methodXXX , 那么 methodXXX 就是通过 genMethod 包装之后的函数。我们来看一下 genMethod 。

js
function genMethod(moduleID: number, methodID: number, type: MethodType) {q
    let fn = function nonPromiseMethodWrapper(...args: Array<mixed>) {
      /* 同步调用 */
      if (type === 'sync') {
        return BatchedBridge.callNativeSyncHook(
          moduleID,
          methodID,
          newArgs,
          onFail,
          onSuccess,
        );
      /* 异步调用 */  
      } else {
        BatchedBridge.enqueueNativeCall(
          moduleID,
          methodID,
          newArgs,
          onFail,
          onSuccess,
        );
      }
    };
  fn.type = type;
  return fn;
}

这里简化了 genMethod 的流程,如上 fn 就是 methodXXX 方法。异步调用 methodXXX 本质上就是调用 enqueueNativeCall 方法,同步的话调用的是 callNativeSyncHook 方法。

enqueueNativeCall 方法也是 MessageQueue 类上的方法。 这个方法会把事件放入到队列中,在短时间内,会有 Native 处理事件,如果超出一定时间阈值没有被处理,那么会执行 JSIExecutor 约定好的 nativeFlushQueueImmediate 方法。这里还有一个细节,就是在调用 enqueueNativeCall 方法的时候,会调用 processCallbacks, 把回调函数放在 _successCallbacks 上。

js
enqueueNativeCall(){
    /* 把回调函数放在 _successCallbacks 上 */
    this.processCallbacks(moduleID, methodID, params, onFail, onSucc);
    /* 调用 JSIExecutor 约定好的 nativeFlushQueueImmediate */
    global.nativeFlushQueueImmediate(queue);
}

接下来看一下 C++ JSI 是怎么在 global 定义这个方法的:

c++
void JSIExecutor::initializeRuntime() {
    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) {
            /* 调用 callNativeModules 方法 */
            callNativeModules(args[0], false);
            return Value::undefined();
          }));
}

在初始化 JS 运行时的时候,会将 global 上定义 nativeFlushQueueImmediate,本质上调用 JSI 中的 callNativeModules 方法。

c++
  void callNativeModules()
     //...
    for (auto &call : methodCalls) {
      m_registry->callNativeMethod(
          call.moduleId, call.methodId, std::move(call.arguments), call.callId);
    }
  }

callNativeModules 会遍历 methodCalls 方法,然后依次调用 ModuleRegistry 上的 callNativeMethod 方法,传入的是 moduleId, methodId,参数,以及回调函数 id 。

在 callNativeMethod 中会通过 moduleId 找到 NativeModules 上对应的 NativeModule, 并执行 invoke 方法。如下所示:

c++
void ModuleRegistry::callNativeMethod() {
  /* 通过 moduleId 找到 NativeModules 上对应的 NativeModule, 并执行 invoke 方法 */
  modules_[moduleId]->invoke(methodId, std::move(params), callId);
}

C++ 中调用 invoke ,会通过映射的方式最终调用的是 JavaMethodWrapper 上的 invoke 方法。

java
@Override
  public void invoke(JSInstance jsInstance, ReadableArray parameters) {
      try {
        mMethod.invoke(mModuleWrapper.getModule(), mArguments);
      }
  }

在 JavaMethodWrapper invoke 上会通过代理的方式,调用 Native 侧的时间处理函数。到此完成 JS 向 Native 的通信。

回调函数处理:

js
NativeCommonModule.callNativeMethod('methodXXX',handler)

当如果是通过 NativeCommonModule.callNativeMethod 调用,那么就会有对应的回调函数 handler 。那么本质上 Native 得到结果之后,还要向 JS 通信,并传递 Native 的结果,也就是这么一来一回的过程。

如果是有回调函数的形式,那么在 Java 端,会有专门的 CallbackImpl 类处理回调函数的情况。

来看一下具体实现:

java
@Override
public void invoke(Object... args) {
    mJSInstance.invokeCallback(mCallbackId, Arguments.fromJavaArgs(args));
    mInvoked = true;
}

在这个方法中,会向 mJSInstance 的 invokeCallback 传入返回值和回调函数 id 。

java
@Override
public void invokeCallback(final int callbackID, final NativeArrayInterface arguments) {
    jniCallJSCallback(callbackID, (NativeArray) arguments);
  }

invokeCallback 方法直接调用 C++ CatalystInstanceImpl 中的 jniCallJSCallback 方法。jniCallJSCallback 中的逻辑也非常简单:

c++
void CatalystInstanceImpl::jniCallJSCallback(
    jint callbackId,
    NativeArray *arguments) {
  instance_->callJSCallback(callbackId, arguments->consume());
}

主要就是调用 instance 下面的 callJSCallback , 传入结果及回调函数 id 。

c++
void Instance::callJSCallback(uint64_t callbackId, folly::dynamic &&params) {
  nativeToJsBridge_->invokeCallback((double)callbackId, std::move(params));
}

callJSCallback 本质上调用 nativeToJsBridge_ 下面的 invokeCallback 方法。接下来就会调用 JsExecutor 下面的 invokeCallback 方法。

c++
void JSIExecutor::invokeCallback(){
    Value ret;
  try {
    ret = invokeCallbackAndReturnFlushedQueue_->call(
        *runtime_, callbackId, valueFromDynamic(*runtime_, arguments));
  } catch (...) {
    //...
  }
}

在 JSIExecutor 的 invokeCallback 方法中,会调用 JS 层 MessageQueue 中的 invokeCallbackAndReturnFlushedQueue 方法。来看一下这个方法。

js
class MessageQueue{
    invokeCallbackAndReturnFlushedQueue(){
       this.__invokeCallback(cbID, args);
    }
    __invokeCallback(cbID, args){
        /* 通过 callID 找到对应的回调函数 */
        const callback = this._successCallbacks.get(callID)
        /* 执行对应的回调函数 */
        callback(...args);
    }
}

invokeCallbackAndReturnFlushedQueue 会调用 __invokeCallback , __invokeCallback 会在 _successCallbacks 取出回调函数,并执行。

上面讲到过在调用桥方法的时候,本质上已经通过 processCallbacks 把回调函数放到 _successCallbacks 上。就此完成整个流程。

完成的通信流程如下:

六 总结

本章节介绍 React Native 的通信原理,分别介绍了 RN 中通信的核心模块,以及完整的介绍了 Native <-> 双向通信流程。在下一章节中,将一起探索 RN Native 渲染的奥秘。

前端知识体系 · wcrane