美文网首页React Native开发技术干货
ReactNative 通信机制_java端源码分析

ReactNative 通信机制_java端源码分析

作者: wo883721 | 来源:发表于2017-11-28 19:41 被阅读179次

    ReactNative: 源码分析系列

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

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

    1. JavaScriptModule:表示js端提供的模型以及方法,java调用模型的方法既可以通知js端,典型的有AppRegistry。java端就是一个接口AppRegistry.js,而js端AppRegistry.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++端通信模块的代码。

    相关文章

      网友评论

        本文标题:ReactNative 通信机制_java端源码分析

        本文链接:https://www.haomeiwen.com/subject/utzsbxtx.html