React Native 源码分析——通信机制

Posted 薛瑄

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React Native 源码分析——通信机制相关的知识,希望对你有一定的参考价值。

本篇来分析一下,RN js和java的通信机制,在上一篇启动流程 看完后,通信的过程,你应该也能猜出个大概。具体过程,也是很简单

1、React Native 源码分析(一)—— 启动流程
2、React Native 源码分析(二)—— 通信机制
3、React Native 源码分析(三)—— Native View创建流程
4、React Native 源码分析(四)—— 任务调度
5、React Native 源码分析(五)—— 事件分发

一、 Java->JS 通信

先来一张图,看一下通信的整体过程
图片来源

这里接着上一篇的代码1.9 reactRoot.runApplication(); 继续分析,从java启动react的过程

1.1 runApplication

  @Override
  public void runApplication() 
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "ReactRootView.runApplication");
    try 
      if (mReactInstanceManager == null || !mIsAttachedToInstance) 
        return;
      

      ReactContext reactContext = mReactInstanceManager.getCurrentReactContext();
      if (reactContext == null) 
        return;
      

      CatalystInstance catalystInstance = reactContext.getCatalystInstance();
      //在自己的activity中,指定的mainComponentName名称
      String jsAppModuleName = getJSModuleName();
      //onMeasure执行后,该变量会设为true,如果再次调用到runApplication,需要重新绘制
      if (mWasMeasured) 
        //本质是会通过FabricUIManager或UIManagerModule 来更新界面
        updateRootLayoutSpecs(true, mWidthMeasureSpec, mHeightMeasureSpec);
      

      WritableNativeMap appParams = new WritableNativeMap();
      appParams.putDouble("rootTag", getRootViewTag());
      @Nullable Bundle appProperties = getAppProperties();
      if (appProperties != null) 
        appParams.putMap("initialProps", Arguments.fromBundle(appProperties));
      

      mShouldLogContentAppeared = true;
      //启动js中的 AppRegistry register的Component,并传入参数appParams
      catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);
     finally 
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
    
  

getJSModule的调用流程catalystInstance.getJSModule ->mJSModuleRegistry.getjavascriptModule,调用到了JavaScriptModuleRegistry中,这里是通过动态代理的方式,让 继承了JavaScriptModule的Interface中的方法的 调用,都被代理到 mCatalystInstance.callFunction

2、JavaScriptModuleRegistry # getJavaScriptModule

public final class JavaScriptModuleRegistry 
  private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances;

  public JavaScriptModuleRegistry() 
    mModuleInstances = new HashMap<>();
  
  //catalystInstance.getJSModule 调到这里
  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(),
                //此时指的是AppRegistry.class
                new Class[] moduleInterface,
                new JavaScriptModuleInvocationHandler(instance, moduleInterface));
    mModuleInstances.put(moduleInterface, interfaceProxy);
    return (T) interfaceProxy;
  

  private static class JavaScriptModuleInvocationHandler implements InvocationHandler 
    private final CatalystInstance mCatalystInstance;
    private final Class<? extends JavaScriptModule> mModuleInterface;
    private @Nullable String mName;

    public JavaScriptModuleInvocationHandler(
        CatalystInstance catalystInstance, Class<? extends JavaScriptModule> moduleInterface) 
      mCatalystInstance = catalystInstance;
      mModuleInterface = moduleInterface;
      ...
    

   ....

    @Override
    public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
        throws Throwable 
      NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
      //getJSModuleName 获取js module的类名,method.getName() 调用的方法名,jsArgs 传入的参数
      //下面分析该函数
      mCatalystInstance.callFunction(getJSModuleName(), method.getName(), jsArgs);
      return null;
    
  

    ....

1.3 CatalystInstance的函数 callFunction 和 runJSBundle

  public void callFunction(PendingJSCall function) 
    if (mDestroyed) 
      final String call = function.toString();
      FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed: " + call);
      return;
    
    //如果js bundle还未加载,就把任务加入到mJSCallsPendingInit,直接返回
    if (!mAcceptCalls) 
      // Most of the time the instance is initialized and we don't need to acquire the lock
      synchronized (mJSCallsPendingInitLock) 
        if (!mAcceptCalls) 
          mJSCallsPendingInit.add(function);
          return;
        
      
    
    function.call(this);
  

mAcceptCalls 的初始值是false,callFunctionrunJSBundle 都是从React Native 源码分析(一)—— 启动流程的代码1.4的 runCreateReactContextOnNewThread,callFunction 是在nativeModule线程运行,由于并发的原因,为了保证js bundle加载后,再去调用js的函数。引入mAcceptCalls 来控制,

  @Override
  public void runJSBundle() 
    //上篇文章代码1.4分析的,mJSBundleLoader 有几种类型,由js bundle的位置来决定
    mJSBundleLoader.loadScript(CatalystInstanceImpl.this);

    synchronized (mJSCallsPendingInitLock) 

      // Loading the bundle is queued on the JS thread, but may not have
      // run yet.  It's safe to set this here, though, since any work it
      // gates will be queued on the JS thread behind the load.
      mAcceptCalls = true;

      for (PendingJSCall function : mJSCallsPendingInit) 
        function.call(this);
      
      mJSCallsPendingInit.clear();
      mJSBundleHasLoaded = true;
    
  

最终都是调用了function.call(this);,下面来分析该函数

1.4 PendingJSCall # call

    void call(CatalystInstanceImpl catalystInstance) 
      NativeArray arguments = mArguments != null ? mArguments : new WritableNativeArray();
      //这里调用到c++ 层
      catalystInstance.jniCallJSFunction(mModule, mMethod, arguments);
    

1.5 CatalystInstanceImpl::jniCallJSFunction

void CatalystInstanceImpl::jniCallJSFunction(
    std::string module,
    std::string method,
    NativeArray *arguments) 
  // We want to share the C++ code, and on ios, modules pass module/method
  // names as strings all the way through to JS, and there's no way to do
  // string -> id mapping on the objc side.  So on android, we convert the
  // number to a string, here which gets passed as-is to JS.  There, they they
  // used as ids if isFinite(), which handles this case, and looked up as
  // strings otherwise.  Eventually, we'll probably want to modify the stack
  // from the JS proxy through here to use strings, too.
  instance_->callJSFunction(
      std::move(module), std::move(method), arguments->consume());

1.6 Instance::callJSFunction

void Instance::callJSFunction(
    std::string &&module,
    std::string &&method,
    folly::dynamic &&params) 
    //该回调是在上篇文章的代码1.7 的new BridgeCallback(this) 创建的
  callback_->incrementPendingJSCalls();
  //该变量 在上篇文章,也有介绍,下面直接看callFunction
  nativeToJsBridge_->callFunction(
      std::move(module), std::move(method), std::move(params));

1.7 NativeToJsBridge::callFunction

void NativeToJsBridge::callFunction(
    std::string &&module,
    std::string &&method,
    folly::dynamic &&arguments) 
   ...

  //把lambda代码发送到js线程队列,所以java->js的通信是异步的
  runOnExecutorQueue([this,
                      module = std::move(module),
                      method = std::move(method),
                      arguments = std::move(arguments),
                      systraceCookie](JSExecutor *executor) 
    if (m_applicationScriptHasFailure) 
    ...
    // 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 这里作为lambda的parameter,argument 传的是m_executor(上篇文章代码2.5 被赋值)
    executor->callFunction(module, method, arguments);
  );

最终调用m_executor ,(本例)也就是JSIExecutor 的callFunction,(也可能是其他的jsexecutor 的callFunction)

1.8 JSIExecutor:: callFunction

void JSIExecutor::callFunction(
    const std::string &moduleId,
    const std::string &methodId,
    const folly::dynamic &arguments) 

  if (!callFunctionReturnFlushedQueue_) 
    //把js 中的函数映射到c++层,例如这里的callFunctionReturnFlushedQueue_
    bindBridge();
  
  ...
  try 
    scopedTimeoutInvoker_(
        [&] 
          //调用js 的同名函数
          ret = callFunctionReturnFlushedQueue_->call(
              *runtime_,
              moduleId,
              methodId,
              valueFromDynamic(*runtime_, arguments));
        ,
        std::move(errorProducer));
   catch (...) 
    std::throw_with_nested(
        std::runtime_error("Error calling " + moduleId + "." + methodId));
  
  //返回值,传到java
  callNativeModules(ret, true);

下面到js看看,callFunctionReturnFlushedQueue_ 的实现

1.9 MessageQueue & callFunctionReturnFlushedQueue

MessageQueue 类的位置在 Libraries/BatchedBridge/MessageQueue.js

  callFunctionReturnFlushedQueue(
    module: string,
    method: string,
    args: mixed[],
  ): null | [Array<number>, Array<number>, Array<mixed>, number] 
    this.__guard(() => 
       //找到对应的module,调用方法
      this.__callFunction(module, method, args);
    );
    //这个会调用 JSTimer 的callImmediates
    return this.flushedQueue();
  

  __callFunction(module: string, method: string, args: mixed[]): void 
    this._lastFlush = Date.now();
    this._eventLoopStartTime = this._lastFlush;
    ...
    //从_lazyCallableModules 中取出对应的module
    const moduleMethods = this.getCallableModule(module);
    ...
    //moduleMethods[method] 方法名,用 apply 来调用 
    moduleMethods[method].apply(moduleMethods, args);
   
  

至此,java-> js 的通信流程就分析完了,下面来看看js->java的

二、 JS->Java 通信

在开始分析之前,首先要知道,JS->Java 通信是两种的,表现在通信中的差异,下面会介绍一些。具体原因是传递的 JavaScriptExecutorFactory不同(可在ReactNativeHost中设置),但是 如果你本地跑了metro,那么会自动使用 ProxyJavaScriptExecutor(可自己翻阅代码 ReactInstanceManager 的onReloadWithJSDebugger函数)

我们在JS中调用Java的方法,是通过这样的形式

  1. 继承 ReactContextBaseJavaModule,使用@ReactMethod 注解 暴露给js的方法,
  2. 把 上一步创建的类 添加到ReactPackage
  3. 把ReactPackage添加到ReactNativeHost 的getPackages() 的返回值中
  4. 在js侧,通过NativeModules.xxxModuleName.xxxMethodName() 调用到原生

下面我们就从 NativeModules.xxxModuleName 这样的代码开始分析:

先来看张图,对整体过程,有个感觉。图片来源

首先需要搞清楚,这个NativeModules 是什么,在js侧,代码路径 Libraries/BatchedBridge/NativeModules.js

2.1、 NativeModules.js

let NativeModules: [moduleName: string]: $FlowFixMe, ... = ;
if (global.nativeModuleProxy) 
  //nativeModuleProxy 的赋值,是在 JSIExecutor::initializeRuntime() ,详见上篇文章
  //如果执行到这里,那么JavaScriptExecutor 使用的是JSIExecutor
  NativeModules = global.nativeModuleProxy;
 else if (!global.nativeExtensions) 

  //如果你是debug,本地跑metro服务,会运行到这里
  const bridgeConfig = global.__fbBatchedBridgeConfig;

  const defineLazyObjectProperty = require('../Utilities/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),
        );
      
    ,
  );


module.exports = NativeModules;

不同的JavaScriptExecutor 最终都会执行到genModule,下面我们分析JSIExecutor 分支 的流程。

简单回忆一下,在 JSIExecutor::initializeRuntime() 中设置了 global.nativeModuleProxy 的值,

  runtime_->global().setProperty(
      *runtime_,
      "nativeModuleProxy",
      Object::createFromHostObject(
          *runtime_, std::make_shared<NativeModuleProxy>(nativeModules_)));

可以看到global.nativeModuleProxy 是NativeModuleProxy 类型,在获取对应的module,会执行NativeModuleProxy的get方法,下面来到c++层:

2.2、 JSIExecutor::NativeModuleProxy

class JSIExecutor::NativeModuleProxy : public jsi::HostObject 
 public:
  NativeModuleProxy(std::shared_ptr<JSINativeModules> nativeModules)
      : weakNativeModules_(nativeModules) 

  Value get(Runtime &rt, const PropNameID &name) override 
     ...
    auto nativeModules = weakNativeModules_.lock();
    if (!nativeModules) 
      return nullptr;
    
    //调用 JSIExecutor的getModule
    return nativeModules->getModule(rt, name);
  

 ...

 private:
  std::weak_ptr<JSINativeModules> weakNativeModules_;
;

2.3、 JSIExecutor # getModule

Value JSINativeModules::getModule(Runtime &rt, const PropNameID &name) 
  if (!m_moduleRegistry) 
    return nullptr;
  
  
  std::string moduleName = name.utf8(rt);
  //先从缓存中查找,如果已经被加载过,直接返回
  const auto it = m_objects.find(moduleName);
  if (it != m_objects.end()) 
    ...
    return Value(rt, it->second);
  
  // 缓存没有命中,创建module。
  // 创建module,解析module对应的method,为每个method创建对应的js方法(用于把对应的调用信息,发送到js线程队列)
  auto module = createModule(rt, moduleName);
  ...省略判空逻辑...
  // 把创建的module缓存起来
  auto result = m_objects.emplace(std::move(moduleName), std::move(*module)).first;
  
  Value ret = Value(rt, result->second);
  //直接返回给js了
  return ret;

createModule 是核心,下面来分析

2.4 JSINativeModules::createModule

folly::Optional<Object> JSINativeModules::createModule(
    Runtime &rt,
    const std::string &name) 
  //获取到js层的__fbGenNativeModule ,在NativeModule.js中有这句 global.__fbGenNativeModule = genModule;
  if (!m_genNativeModuleJS) 
    m_genNativeModuleJS =
        rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");以上是关于React Native 源码分析——通信机制的主要内容,如果未能解决你的问题,请参考以下文章

React Native 源码分析——通信机制

React Native 源码分析——通信机制

React Native 源码分析——通信机制

React Native之底层源码分析篇

React Native之底层源码分析篇

React Native 源码分析——Native View创建流程