ReactNative 通信机制_java端源码分析

ReactNative: 源码分析系列

ReactNative 启动过程源码分析
ReactNative 通信机制_java端源码分析
ReactNative 通信机制_c++端源码分析
ReactNative 通信机制_js端源码分析

通信机制主要分成三部分,java端,c++端以及js端。调用的方式分成两种JavaScriptModule和NativeModule。

  1. JavaScriptModule:表示 js 端提供的模型以及方法,java调用模型的方法既可以通知js端,典型的有AppRegistry。java端就是一个接口 AppRegistry ,而 jsAppRegistry.js 提供了具体操作。
  2. NativeModule:表示java端提供了模型和方法,供js端来回调。

这一节我们主要分析java端的源码:

JavaScriptModuleRegistry

通过动态代理的方式,回调CatalystInstance.callFunction方法,然后调用CatalystInstance.jniCallJSFunction方法,从而调用CatalystInstance.cpp对应jniCallJSFunction,最终调用到js端MessageQueue.js的__callFunction方法,然后就可以找到js端JavaScriptModule对应的方法并调用。

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;
  }

通过JavaScriptModule的class来获取它的代理实例。

我们并不需要JavaScriptModule的真正的实例,因为它的实现是在js端,java端只需要提供对应的module模板名,method方法名以及args调用参数,就可以回调js端对应的方法了。

JavaScriptModuleInvocationHandler

@Override
    public @Nullable Object invoke(Object proxy, Method method, 
            @Nullable Object[] args) throws Throwable {
      NativeArray jsArgs = args != null
        ? Arguments.fromJavaArgs(args)
        : new WritableNativeArray();
      mCatalystInstance.callFunction(getJSModuleName(), method.getName(), jsArgs);
      return null;
    }

当我们用得到的代理实例JavaScriptModule调用方法时,就会回调这个方法。

例如catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);就会调用这个方法。

这个方法会调用mCatalystInstance.callFunction方法,提供JavaScriptModule的module、method、args。从而回调jniCallJSFunction方法,最终会调用到js端JavaScriptModule对应方法。

NativeModuleRegistry

向c++端提供java端所有的NativeModule。主要是两个方法:getJavaModules和getCxxModules。这个两个方法都是c++端直接调用的。

  /* package */ Collection<JavaModuleWrapper> getJavaModules(
      JSInstance jsInstance) {
    ArrayList<JavaModuleWrapper> javaModules = new ArrayList<>();
    for (Map.Entry<Class<? extends NativeModule>, ModuleHolder> entry : 
          mModules.entrySet()) {
      Class<? extends NativeModule> type = entry.getKey();
      if (!CxxModuleWrapperBase.class.isAssignableFrom(type)) {
        javaModules.add(new JavaModuleWrapper(jsInstance, type, entry.getValue()));
      }
    }
    return javaModules;
  }

返回一个JavaModuleWrapper的集合。

接受一个参数JSInstance,它是一个接口,只有一个invokeCallback方法,作用是向js端传递数据。考虑下面情况,我们在js端调用NativeModule的方法,如果这是一个异步方法,那么这个方法的结果值怎么回传给js端呢,就是通过这个JSInstance(注意如果是同步方法就不用了,因为同步方法结果值是直接返回的)。

遍历所有的mModules,排除所有CxxModuleWrapperBase子类的NativeModule,然后创建JavaModuleWrapper实例添加到集合中。

JavaModuleWrapper

主要是向c++端这个NativeModule所有的方法说明,Constants常量,以及接受c++回调。

@DoNotStrip
  public List<MethodDescriptor> getMethodDescriptors() {
    if (mDescs.isEmpty()) {
      findMethods();
    }
    return mDescs;
  }

这个方法是c++端调用的,返回这个NativeModule的所有被@ReactMethod注释方法的描述。

mDescs和mMethods这两个集合大小相同,储存的方法也是一一对应的,mDescs返回给c++端,所以c++就可以集合下标索引找到mMethods对应的方法,就可以进行回调了。

  @DoNotStrip
  private void findMethods() {
    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "findMethods");
    Set<String> methodNames = new HashSet<>();

    Class<? extends NativeModule> classForMethods = mModuleClass;
    Class<? extends NativeModule> superClass =
        (Class<? extends NativeModule>) mModuleClass.getSuperclass();
    if (ReactModuleWithSpec.class.isAssignableFrom(superClass)) {
      // For java module that is based on generated flow-type spec, inspect the
      // spec abstract class instead, which is the super class of the given java
      // module.
      classForMethods = superClass;
    }
    Method[] targetMethods = classForMethods.getDeclaredMethods();

    for (Method targetMethod : targetMethods) {
      ReactMethod annotation = targetMethod.getAnnotation(ReactMethod.class);
      if (annotation != null) {
        String methodName = targetMethod.getName();
        if (methodNames.contains(methodName)) {
          // We do not support method overloading since js sees a function as an object regardless
          // of number of params.
          throw new IllegalArgumentException(
            "Java Module " + getName() + " method name already registered: " + methodName);
        }
        MethodDescriptor md = new MethodDescriptor();
        JavaMethodWrapper method = new JavaMethodWrapper(this, targetMethod, annotation.isBlockingSynchronousMethod());
        md.name = methodName;
        md.type = method.getType();
        if (md.type == BaseJavaModule.METHOD_TYPE_SYNC) {
          md.signature = method.getSignature();
          md.method = targetMethod;
        }
        mMethods.add(method);
        mDescs.add(md);
      }
    }
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
  }

这个方法通过反射,得到NativeModule所有被@ReactMethod注解的方法,创建MethodDescriptor和JavaMethodWrapper存入对应的列表中。

注意当ReactMethod方法是同步方法时,MethodDescriptor会储存这个method和signature,因为当是同步方法时,js端要直接获取java端方法调用的结果值,所以就不能通过invoke方法回调了。c++端拥有方法的引用(就是这个MethodDescriptor的method),直接调用得到结果值,返回给js端。

仔细看这段代码,会发现有个问题。就是methodNames这个集合,它的作用是判断不允许有方法名重复的方法,可惜这个集合从来没有被添加过数据,所以就没有作用。那么为什么不能方法名重复呢,我们都知道java中方法名重复参数不一样,叫做方法重载,但是js是没有方法重载的。所以我们在NativeModule中定义重载方法,js端就只有一个方法,就会出现问题。

  @DoNotStrip
  public @Nullable NativeMap getConstants() {
    if (!mModuleHolder.getHasConstants()) {
      return null;
    }

    final String moduleName = getName();
    SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, 
        "JavaModuleWrapper.getConstants")
      .arg("moduleName", moduleName)
      .flush();
    ReactMarker.logMarker(GET_CONSTANTS_START, moduleName);

    BaseJavaModule baseJavaModule = getModule();

    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "module.getConstants");
    Map<String, Object> map = baseJavaModule.getConstants();
    Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);

    Systrace.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "create WritableNativeMap");
    ReactMarker.logMarker(CONVERT_CONSTANTS_START, moduleName);
    try {
      return Arguments.makeNativeMap(map);
    } finally {
      ReactMarker.logMarker(CONVERT_CONSTANTS_END);
      Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);

      ReactMarker.logMarker(GET_CONSTANTS_END);
      SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
    }
  }

这个方法也是由c++端调用,通过调用getConstants()得到NativeModule的常量,供给js端使用。

@DoNotStrip
  public void invoke(int methodId, ReadableNativeArray parameters) {
    if (mMethods == null || methodId >= mMethods.size()) {
      return;
    }

    mMethods.get(methodId).invoke(mJSInstance, parameters);
  }

也是由c++端调用,来调用NativeModule对应的异步方法(同步方法在c++直接调用,不会走这里)。

methodId表示在集合中的索引(c++和java端方法列表的索引是一一对应的),parameters:js端传递来的参数。mJSInstance:用于将方法的结果值通知给js端。

JavaMethodWrapper

调用NativeModule对应的方法,但是要处理两个问题,第一处理ReadableNativeArray数据将它转成方法对应的参数,第二区分处理NativeModule方法对应的三种类型sync、async、 promise。

  1. sync表示同步方法,js端直接得到java端方法调用的返回值。
  2. async和promise都是异步方法,它们本质上都是通过Callback.java这个类的invoke方法,回调JSInstance的invokeCallback方法,调用子类CatalystInstanceImpl的invokeCallback方法,从而调用c++的jniCallJSCallback方法,最终调用js端MessageQueue.js的__invokeCallback方法,将java端方法调用结果值返回给js端。只不过promise方式对Callback.java进行了包装(具体参考PromiseImpl.java)
private static abstract class ArgumentExtractor<T> {
    public int getJSArgumentsNeeded() {
      return 1;
    }

    public abstract @Nullable T extractArgument(
      JSInstance jsInstance, ReadableNativeArray jsArguments, int atIndex);
  }

它是用来提取ReadableNativeArray中的参数。

  static final private ArgumentExtractor<Callback> ARGUMENT_EXTRACTOR_CALLBACK =
    new ArgumentExtractor<Callback>() {
      @Override
      public @Nullable Callback extractArgument(
        JSInstance jsInstance, ReadableNativeArray jsArguments, int atIndex) {
        if (jsArguments.isNull(atIndex)) {
          return null;
        } else {
          int id = (int) jsArguments.getDouble(atIndex);
          return new com.facebook.react.bridge.CallbackImpl(jsInstance, id);
        }
      }
    };

id:这个参数js端使用,来确定返回值给对应module的调用。CallbackImpl这个类主要会调用invokeCallback方法,最终会调用到MessageQueue.js的__invokeCallback方法。

static final private ArgumentExtractor<Promise> ARGUMENT_EXTRACTOR_PROMISE =
    new ArgumentExtractor<Promise>() {
      @Override
      public int getJSArgumentsNeeded() {
        return 2;
      }

      @Override
      public Promise extractArgument(
        JSInstance jsInstance, ReadableNativeArray jsArguments, int atIndex) {
        Callback resolve = ARGUMENT_EXTRACTOR_CALLBACK
          .extractArgument(jsInstance, jsArguments, atIndex);
        Callback reject = ARGUMENT_EXTRACTOR_CALLBACK
          .extractArgument(jsInstance, jsArguments, atIndex + 1);
        return new PromiseImpl(resolve, reject);
      }
    };

它需要消耗的参数是两个,因为有正确的回调和异常的回调,返回一个PromiseImpl实例,它是Promise子类,当我们调用resolve或者reject方法时,都会调用CallbackImpl的invokeCallback,来通知js端。

private void processArguments() {
    if (mArgumentsProcessed) {
      return;
    }
    SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, "processArguments")
      .arg("method", mModuleWrapper.getName() + "." + mMethod.getName())
      .flush();
    try {
      mArgumentsProcessed = true;
      mArgumentExtractors = buildArgumentExtractors(mParameterTypes);
      mSignature = buildSignature(
          mMethod,
          mParameterTypes,
          (mType.equals(BaseJavaModule.METHOD_TYPE_SYNC)));
      mArguments = new Object[mParameterTypes.length];
      mJSArgumentsNeeded = calculateJSArgumentsNeeded();
    } finally {
      SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
    }
  }

通过反射,获取方法的参数类型列表,来生成mArgumentExtractors列表,用来解析ReadableNativeArray。

 @Override
 @Override
  public void invoke(JSInstance jsInstance, ReadableNativeArray parameters) {
    String traceName = mModuleWrapper.getName() + "." + mMethod.getName();
    SystraceMessage.beginSection(TRACE_TAG_REACT_JAVA_BRIDGE, 
          "callJavaModuleMethod")
      .arg("method", traceName)
      .flush();
    if (DEBUG) {
      PrinterHolder.getPrinter()
          .logMessage(
              ReactDebugOverlayTags.BRIDGE_CALLS,
              "JS->Java: %s.%s()",
              mModuleWrapper.getName(),
              mMethod.getName());
    }
    try {
      if (!mArgumentsProcessed) {
        processArguments();
      }
      if (mArguments == null || mArgumentExtractors == null) {
        throw new Error("processArguments failed");
      }
      if (mJSArgumentsNeeded != parameters.size()) {
        throw new NativeArgumentsParseException(
          traceName + " got " + parameters.size() 
          + " arguments, expected " + mJSArgumentsNeeded);
      }

      int i = 0, jsArgumentsConsumed = 0;
      try {
        for (; i < mArgumentExtractors.length; i++) {
          mArguments[i] = mArgumentExtractors[i].extractArgument(
            jsInstance, parameters, jsArgumentsConsumed);
          jsArgumentsConsumed += mArgumentExtractors[i].getJSArgumentsNeeded();
        }
      } catch (UnexpectedNativeTypeException e) {
        throw new NativeArgumentsParseException(
          e.getMessage() + " (constructing arguments for " + traceName 
            + " at argument index " +
            getAffectedRange(jsArgumentsConsumed, 
             mArgumentExtractors[i].getJSArgumentsNeeded()) +
            ")",
          e);
      }

      try {
        mMethod.invoke(mModuleWrapper.getModule(), mArguments);
      } catch (IllegalArgumentException ie) {
        throw new RuntimeException("Could not invoke " + traceName, ie);
      } catch (IllegalAccessException iae) {
        throw new RuntimeException("Could not invoke " + traceName, iae);
      } catch (InvocationTargetException ite) {
        if (ite.getCause() instanceof RuntimeException) {
          throw (RuntimeException) ite.getCause();
        }
        throw new RuntimeException("Could not invoke " + traceName, ite);
      }
    } finally {
      SystraceMessage.endSection(TRACE_TAG_REACT_JAVA_BRIDGE).flush();
    }
  }

将parameters转换成method对应的参数,然后通过 mMethod.invoke(mModuleWrapper.getModule(), mArguments)进行方法调用。

CatalystInstanceImpl

通过这个类主动调用c++端方法

 private native void initializeBridge(
      ReactCallback callback,
      JavaScriptExecutor jsExecutor,
      MessageQueueThread jsQueue,
      MessageQueueThread moduleQueue,
      MessageQueueThread uiBackgroundQueue,
      Collection<JavaModuleWrapper> javaModules,
      Collection<ModuleHolder> cxxModules);

调用CatalystInstanceImpl.cpp中对应方法。

  1. ReactCallback:用于c++回调。
  2. jsExecutor:js执行器,对应JSCExecutor.cpp实例。
  3. jsQueue、moduleQueue、uiBackgroundQueue用c++不同线程中的回调。
  4. javaModules、cxxModules将NativeModule传递给C++。
private native void jniCallJSFunction(
    String module,
    String method,
    NativeArray arguments);

这个方法用来调用js端JavaScriptModule对应方法。
当在java端调用JavaScriptModule方法时,

例如catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams))

就会调用到JavaScriptModuleRegistry中JavaScriptModuleInvocationHandler内部类的invoke方法,调用CatalystInstanceImpl的callFunction方法,接着调用PendingJSCall的call方法,就会调用到jniCallJSFunction这个方法了。

private native void jniCallJSCallback(int callbackID, NativeArray arguments);

当js端调用NativeModule的异步方法时,通过这个方法返回java端方法调用的结果值的。
java端NativeModule对应方法调用完成后,我们手动调用Callback的invoke方法

这个Callback是我们自定义方法形参,如果没有定义它或者Promise,那么就表示不需要返回给js端数据,因为没办法将js端传递值。这里指的是异步方法.。

进而调用JSInstance的invokeCallback方法,然后就会回调到jniCallJSCallback这个方法。

总结

JavaScriptModule:

java端使用module名,method名通过jniCallJSFunction( String module, String method, NativeArray arguments)方法调用js端JavaScriptModule对应的方法。

注意:这种方式不能得到js端方法调用的返回值的。其实从c++和js端源码看出它是有一个可以得到返回值的方法,可惜java端并没有此方法的调用,具体会在接下来两章中分析。

NativeModule:

js端调用java端方法的方式,对应js端NativeModules.js。分为同步方法和异步方法:

  1. 同步方法会在c++端直接调用,得到结果值,返回给js端。
  2. 异步方法由c++端调用JavaModuleWrapper的invoke方法,再调用JavaMethodWrapper的invoke方法,最终会调用到CatalystInstanceImpl的jniCallJSCallback方法,最后会调用到MessageQueue.js的__invokeCallback方法。

下节预览

java端通信模块的代码已经分析完了,下面是c++端通信模块的代码。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • ReactNative: 源码分析系列 ReactNative 启动过程源码分析ReactNative 通信机制_...
    wo883721阅读 1,184评论 0 0
  • ReactNative: 源码分析系列 ReactNative 启动过程源码分析ReactNative 通信机制_...
    wo883721阅读 1,637评论 3 1
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 2015年10月以前,每天公司和家两点一线的生活,起早贪黑地工作、常常因低效而顾不上吃早餐、午餐晚餐也不规律,导致...
    五彩云霞618阅读 1,240评论 2 8
  • 分享一个小故事。一个五年级的小女孩儿,因为喜欢同班的男同学,所以周末和好朋友一起跑到男同学家的大棚里帮忙拔草,吓坏...
    立禾禾阅读 631评论 0 1