React Native通信原理源码分析一

作者: juexingzhe | 来源:发表于2018-04-08 15:13 被阅读129次

    小伙伴们都知道在Android开发中实现Java和JS的通信可以通过WebView来实现,包括注册JSBridge或者在接口中拦截都可以。然而React Native中并没有用WebView控件的方式,而是基于WebKit内核的方式来实现Java与JS的通信,也就是Java与JS之间的通信都是通过中间层C++来实现的,今天我们来分析下Android中React Native怎么实现Java和JS之间的通信。

    React Native中调用所有的行为都是从Native端发起的,用户操作直接面向的也是Native。所以这个通信模型又可以看成是Native发起会话,然后Javascript进行应答。

    Java要能调用到JS需要在Java层注册JS模块,先来看下JS模块的注册过程。

    1.JavaScriptModule模块注册(Java层)

    系统注册了一些常用的JS模块,在CoreModulesPackage的createJSModules中注册了一些系统核心的JS模块,比如AppRegistry(RN组件注册模块)/RCTEventEmitter(事件发射模块)等。

    class CoreModulesPackage implements ReactPackage{
      @Override
      public List<NativeModule> createNativeModules(
          ReactApplicationContext catalystApplicationContext) {
        ......
        return Arrays.<NativeModule>asList(
            new AnimationsDebugModule(
                catalystApplicationContext,
                mReactInstanceManager.getDevSupportManager().getDevSettings()),
            new AndroidInfoModule(),
            new DeviceEventManagerModule(catalystApplicationContext, mHardwareBackBtnHandler),
            new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager()),
            new Timing(catalystApplicationContext),
            new SourceCodeModule(
                mReactInstanceManager.getSourceUrl(),
                mReactInstanceManager.getDevSupportManager().getSourceMapUrl()),
            uiManagerModule,
            new DebugComponentOwnershipModule(catalystApplicationContext));
      }
      
      @Override
      public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Arrays.asList(
            DeviceEventManagerModule.RCTDeviceEventEmitter.class,
            JSTimersExecution.class,
            RCTEventEmitter.class,
            RCTNativeAppEventEmitter.class,
            AppRegistry.class,
            com.facebook.react.bridge.Systrace.class,
            DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);
      }
    }
    
    public interface ReactPackage {
    
      /**
       * @param reactContext react application context that can be used to create modules
       * @return list of native modules to register with the newly created catalyst instance
       */
      List<NativeModule> createNativeModules(ReactApplicationContext reactContext);
    
      /**
       * @return list of JS modules to register with the newly created catalyst instance.
       *
       * IMPORTANT: Note that only modules that needs to be accessible from the native code should be
       * listed here. Also listing a native module here doesn't imply that the JS implementation of it
       * will be automatically included in the JS bundle.
       */
      List<Class<? extends JavaScriptModule>> createJSModules();
    
      /**
       * @return a list of view managers that should be registered with {@link UIManagerModule}
       */
      List<ViewManager> createViewManagers(ReactApplicationContext reactContext);
    }
    

    以RCTEventEmitter这个JavaScriptModule模块为例来看下JS模块在Java层的实现。

    所有JS层组件实现JavaScriptModule接口,比如RCTEventEmitter:

    public interface RCTEventEmitter extends JavaScriptModule {
      public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
      public void receiveTouches(
          String eventName,
          WritableArray touches,
          WritableArray changedIndices);
    }
    

    可以看出来JS模块在Java层只是简单的继承JavaScriptModule,并没有具体的实现,那么问题就来了,光是一个接口是没法调用的,好像到这里就跟踪不下去了?其实可以换种思路,JS模块肯定是要在Native中注册的,我们先到ReactInstanceManagerImpl中的createReactContext方法中去看下怎么注册的。

    所有的NativeModule和JSModule在ReactInstanceManagerImpl中注册

    方法的代码比较多,但是逻辑不复杂,关键的几个步骤我都在代码中直接做了注释,就不多说了,这里主要分析JS模块的注册,所以我们看到JavaScriptModulesConfig中。

    private ReactApplicationContext createReactContext(
          JavaScriptExecutor jsExecutor,
          JSBundleLoader jsBundleLoader) {
        FLog.i(ReactConstants.TAG, "Creating react context.");
      //Native模块
        NativeModuleRegistry.Builder nativeRegistryBuilder = new NativeModuleRegistry.Builder();
      //JS模块
        JavaScriptModulesConfig.Builder jsModulesBuilder = new JavaScriptModulesConfig.Builder();
    
      //React Context
        ReactApplicationContext reactContext = new ReactApplicationContext(mApplicationContext);
        if (mUseDeveloperSupport) {
          reactContext.setNativeModuleCallExceptionHandler(mDevSupportManager);
        }
    
      //处理CoreModules---包含Native模块和JS模块
        Systrace.beginSection(
            Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
            "createAndProcessCoreModulesPackage");
        try {
          CoreModulesPackage coreModulesPackage =
              new CoreModulesPackage(this, mBackBtnHandler, mUIImplementationProvider);
          processPackage(coreModulesPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
        } finally {
          Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
        }
    
        // 处理用户注册的模块
        for (ReactPackage reactPackage : mPackages) {
          Systrace.beginSection(
              Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
              "createAndProcessCustomReactPackage");
          try {
            processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
          } finally {
            Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
          }
        }
    
      //buildNativeModuleRegistry
        Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildNativeModuleRegistry");
        NativeModuleRegistry nativeModuleRegistry;
        try {
           nativeModuleRegistry = nativeRegistryBuilder.build();
        } finally {
          Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
        }
    
      //buildJSModuleConfig
        Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "buildJSModuleConfig");
        JavaScriptModulesConfig javaScriptModulesConfig;
        try {
          javaScriptModulesConfig = jsModulesBuilder.build();
        } finally {
          Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
        }
    
      ...
        //createCatalystInstance---Java/JS/C++三方通信总管
        CatalystInstanceImpl.Builder catalystInstanceBuilder = new      CatalystInstanceImpl.Builder()
            .setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
            .setJSExecutor(jsExecutor)
            .setRegistry(nativeModuleRegistry)
            .setJSModulesConfig(javaScriptModulesConfig)
            .setJSBundleLoader(jsBundleLoader)
            .setNativeModuleCallExceptionHandler(exceptionHandler);
    
        Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
        CatalystInstance catalystInstance;
        try {
          catalystInstance = catalystInstanceBuilder.build();
        } finally {
          Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
        }
    
        if (mBridgeIdleDebugListener != null) {
          catalystInstance.addBridgeIdleDebugListener(mBridgeIdleDebugListener);
        }
    
        reactContext.initializeWithInstance(catalystInstance);
    
      //runJSBundle
        Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "runJSBundle");
        try {
          catalystInstance.runJSBundle();
        } finally {
          Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
        }
    
        ReactMarker.logMarker("CREATE_REACT_CONTEXT_END");
        return reactContext;
      }
    

    看到JavaScriptModulesConfig,主要是对每个JavaScriptModule构造JavaScriptModuleRegistration,存放在mModules中

    /**
     * Class stores configuration of javascript modules that can be used across the bridge
     */
    public class JavaScriptModulesConfig {
    
      private final List<JavaScriptModuleRegistration> mModules;
    
      private JavaScriptModulesConfig(List<JavaScriptModuleRegistration> modules) {
        mModules = modules;
      }
    
      /*package*/ List<JavaScriptModuleRegistration> getModuleDefinitions() {
        return mModules;
      }
    
      /*package*/ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
        jg.writeStartObject();
        for (JavaScriptModuleRegistration registration : mModules) {
          jg.writeObjectFieldStart(registration.getName());
          appendJSModuleToJSONObject(jg, registration);
          jg.writeEndObject();
        }
        jg.writeEndObject();
      }
    
      private void appendJSModuleToJSONObject(
          JsonGenerator jg,
          JavaScriptModuleRegistration registration) throws IOException {
        jg.writeObjectField("moduleID", registration.getModuleId());
        jg.writeObjectFieldStart("methods");
        for (Method method : registration.getMethods()) {
          jg.writeObjectFieldStart(method.getName());
          jg.writeObjectField("methodID", registration.getMethodId(method));
          jg.writeEndObject();
        }
        jg.writeEndObject();
      }
    
      public static class Builder {
    
        private int mLastJSModuleId = 0;
        private List<JavaScriptModuleRegistration> mModules =
            new ArrayList<JavaScriptModuleRegistration>();
    
        public Builder add(Class<? extends JavaScriptModule> moduleInterfaceClass) {
          int moduleId = mLastJSModuleId++;
          mModules.add(new JavaScriptModuleRegistration(moduleId, moduleInterfaceClass));
          return this;
        }
    
        public JavaScriptModulesConfig build() {
          return new JavaScriptModulesConfig(mModules);
        }
      }
    }
    
    /**
     * Registration info for a {@link JavaScriptModule}. 
        Maps its methods to method ids.
     */
    class JavaScriptModuleRegistration {
    
      private final int mModuleId;
      private final Class<? extends JavaScriptModule> mModuleInterface;
      private final Map<Method, Integer> mMethodsToIds;
      private final Map<Method, String> mMethodsToTracingNames;
    
      JavaScriptModuleRegistration(int moduleId, Class<? extends JavaScriptModule> moduleInterface) {
        mModuleId = moduleId;
        mModuleInterface = moduleInterface;
    
        mMethodsToIds = MapBuilder.newHashMap();
        mMethodsToTracingNames = MapBuilder.newHashMap();
        final Method[] declaredMethods = mModuleInterface.getDeclaredMethods();
        Arrays.sort(declaredMethods, new Comparator<Method>() {
          @Override
          public int compare(Method lhs, Method rhs) {
            return lhs.getName().compareTo(rhs.getName());
          }
        });
    
        // Methods are sorted by name so we can dupe check and have obvious ordering
        String previousName = null;
        for (int i = 0; i < declaredMethods.length; i++) {
          Method method = declaredMethods[i];
          String name = method.getName();
          Assertions.assertCondition(
              !name.equals(previousName),
              "Method overloading is unsupported: " + mModuleInterface.getName() + "#" + name);
          previousName = name;
    
          mMethodsToIds.put(method, i);
          mMethodsToTracingNames.put(method, "JSCall__" + getName() + "_" + method.getName());
        }
      }
      ......
    }
    

    到这里就有了所有的JavaScriptModules,并且都扫描存放在JavaScriptModulesConfig中,那么RN框架是怎么使用的?再回顾前面ReactInstanceManagerImpl,将JavaScriptModulesConfig用来实例化catalystInstance,这个是三方通信的中转站。

     //createCatalystInstance---Java/JS/C++三方通信总管
        CatalystInstanceImpl.Builder catalystInstanceBuilder = new  CatalystInstanceImpl.Builder()
            .setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
            .setJSExecutor(jsExecutor)
            .setRegistry(nativeModuleRegistry)
            .setJSModulesConfig(javaScriptModulesConfig)
            .setJSBundleLoader(jsBundleLoader)
            .setNativeModuleCallExceptionHandler(exceptionHandler);
    
    Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "createCatalystInstance");
        CatalystInstance catalystInstance;
        try {
          catalystInstance = catalystInstanceBuilder.build();
        } finally {
          Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
        }
    

    catalystInstance是一个接口,实现类是CatalystInstanceImpl,跟到构造函数中:

    private CatalystInstanceImpl(
          final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
          final JavaScriptExecutor jsExecutor,
          final NativeModuleRegistry registry,
          final JavaScriptModulesConfig jsModulesConfig,
          final JSBundleLoader jsBundleLoader,
          NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
        ...
        mJSModuleRegistry = new JavaScriptModuleRegistry(CatalystInstanceImpl.this, jsModulesConfig);
        ...
      }
    

    通过JavaScriptModulesConfig实例化JavaScriptModuleRegistry,前面分析过JavaScriptModule在Java层都是implements JavaScriptModule的接口,那么怎么去调用方法?这里就是对每个接口通过动态代理实例化一个代理对象,Java调用JS方法时统一分发到JavaScriptModuleInvocationHandler中的invoke进行处理,在invoke中其实就是调用mCatalystInstance.callFunction

    很明显,接下来需要到JavaScriptModuleRegistry中看看:

    /**
     * Class responsible for holding all the {@link JavaScriptModule}s registered to this
     * {@link CatalystInstance}. 
     Uses Java proxy objects to dispatch method calls on JavaScriptModules
     * to the bridge using the corresponding module and method ids so the proper function is executed in
     * JavaScript.
     */
    /*package*/ class JavaScriptModuleRegistry {
    
      private final HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> mModuleInstances;
    
      public JavaScriptModuleRegistry(
          CatalystInstanceImpl instance,
          JavaScriptModulesConfig config) {
        mModuleInstances = new HashMap<>();
        for (JavaScriptModuleRegistration registration : config.getModuleDefinitions()) {
          Class<? extends JavaScriptModule> moduleInterface = registration.getModuleInterface();
          JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
              moduleInterface.getClassLoader(),
              new Class[]{moduleInterface},
              new JavaScriptModuleInvocationHandler(instance, registration));
    
          mModuleInstances.put(moduleInterface, interfaceProxy);
        }
      }
    
      public <T extends JavaScriptModule> T getJavaScriptModule(Class<T> moduleInterface) {
        return (T) Assertions.assertNotNull(
            mModuleInstances.get(moduleInterface),
            "JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
      }
    
      private static class JavaScriptModuleInvocationHandler implements InvocationHandler {
    
        private final CatalystInstanceImpl mCatalystInstance;
        private final JavaScriptModuleRegistration mModuleRegistration;
    
        public JavaScriptModuleInvocationHandler(
            CatalystInstanceImpl catalystInstance,
            JavaScriptModuleRegistration moduleRegistration) {
          mCatalystInstance = catalystInstance;
          mModuleRegistration = moduleRegistration;
        }
    
        @Override
        public @Nullable Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          String tracingName = mModuleRegistration.getTracingName(method);
          mCatalystInstance.callFunction(
              mModuleRegistration.getModuleId(),
              mModuleRegistration.getMethodId(method),
              Arguments.fromJavaArgs(args),
              tracingName);
          return null;
        }
      }
    }
    

    在Java调用JS模块的方法时,都会通过代理对象调用invoke方法,在invoke方法中调用CatalystInstance.callFunction,CatalystInstance是一个接口,实现类CatalystInstanceImpl,看到CatalystInstanceImpl中的callFunction:

    /* package */ void callFunction(
          final int moduleId,
          final int methodId,
          final NativeArray arguments,
          final String tracingName) {
        if (mDestroyed) {
          FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
          return;
        }
    
        incrementPendingJSCalls();
    
        final int traceID = mTraceID++;
        Systrace.startAsyncFlow(
            Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
            tracingName,
            traceID);
    
        mCatalystQueueConfiguration.getJSQueueThread().runOnQueue(
            new Runnable() {
              @Override
              public void run() {
                mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
    
                Systrace.endAsyncFlow(
                    Systrace.TRACE_TAG_REACT_JAVA_BRIDGE,
                    tracingName,
                    traceID);
    
                if (mDestroyed) {
                  return;
                }
    
                Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, tracingName);
                try {
                  Assertions.assertNotNull(mBridge).callFunction(moduleId, methodId, arguments);
                } finally {
                  Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
                }
              }
            });
      }
    

    所有Java层向Javascript层的通信请求都是走的ReactBridge.callFunction

    其实就是在JSQueueThread线程中调用ReactBridge的callFunction,是Native函数,到这里就从Java转到C++层了

    /**
     * Interface to the JS execution environment and means of transport for messages Java<->JS.
     */
    @DoNotStrip
    public class ReactBridge extends Countable {
    
      /* package */ static final String REACT_NATIVE_LIB = "reactnativejni";
    
      static {
        SoLoader.loadLibrary(REACT_NATIVE_LIB);
      }
    
      private final ReactCallback mCallback;
      private final JavaScriptExecutor mJSExecutor;
      private final MessageQueueThread mNativeModulesQueueThread;
    
      /**
       * @param jsExecutor the JS executor to use to run JS
       * @param callback the callback class used to invoke native modules
       * @param nativeModulesQueueThread the MessageQueueThread the callbacks should be invoked on
       */
      public ReactBridge(
          JavaScriptExecutor jsExecutor,
          ReactCallback callback,
          MessageQueueThread nativeModulesQueueThread) {
        mJSExecutor = jsExecutor;
        mCallback = callback;
        mNativeModulesQueueThread = nativeModulesQueueThread;
        initialize(jsExecutor, callback, mNativeModulesQueueThread);
      }
    
      @Override
      public void dispose() {
        mJSExecutor.close();
        mJSExecutor.dispose();
        super.dispose();
      }
    
      public void handleMemoryPressure(MemoryPressure level) {
        switch (level) {
          case MODERATE:
            handleMemoryPressureModerate();
            break;
          case CRITICAL:
            handleMemoryPressureCritical();
            break;
          default:
            throw new IllegalArgumentException("Unknown level: " + level);
        }
      }
    
      private native void initialize(
          JavaScriptExecutor jsExecutor,
          ReactCallback callback,
          MessageQueueThread nativeModulesQueueThread);
    
      /**
       * All native functions are not thread safe and appropriate queues should be used
       */
      public native void loadScriptFromAssets(AssetManager assetManager, String assetName);
      public native void loadScriptFromFile(@Nullable String fileName, @Nullable String sourceURL);
      public native void callFunction(int moduleId, int methodId, NativeArray arguments);
      public native void invokeCallback(int callbackID, NativeArray arguments);
      public native void setGlobalVariable(String propertyName, String jsonEncodedArgument);
      public native boolean supportsProfiling();
      public native void startProfiler(String title);
      public native void stopProfiler(String title, String filename);
      private native void handleMemoryPressureModerate();
      private native void handleMemoryPressureCritical();
    }
    

    再回头看看ReactBridge在CatalystInstanceImpl里面的初始化,初始化会ReactBridge调用setGlobalVariable,这是个Native函数,是在C++层注册用的,先按下后面再分析组件调用过程再分析,先看看 buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig)做了什么。

    private CatalystInstanceImpl(
          final CatalystQueueConfigurationSpec catalystQueueConfigurationSpec,
          final JavaScriptExecutor jsExecutor,
          final NativeModuleRegistry registry,
          final JavaScriptModulesConfig jsModulesConfig,
          final JSBundleLoader jsBundleLoader,
          NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
        ......
        try {
          mBridge = mCatalystQueueConfiguration.getJSQueueThread().callOnQueue(
              new Callable<ReactBridge>() {
                @Override
                public ReactBridge call() throws Exception {
                  Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
                  try {
                    return initializeBridge(jsExecutor, jsModulesConfig);
                  } finally {
                    Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
                  }
                }
              }).get(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS);
        } catch (Exception t) {
          throw new RuntimeException("Failed to initialize bridge", t);
        }
      }
    
      private ReactBridge initializeBridge(
          JavaScriptExecutor jsExecutor,
          JavaScriptModulesConfig jsModulesConfig) {
        mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
        Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
    
        Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
        ReactBridge bridge;
        try {
          bridge = new ReactBridge(
              jsExecutor,
              new NativeModulesReactCallback(),
              mCatalystQueueConfiguration.getNativeModulesQueueThread());
        } finally {
          Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
        }
    
        Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
        try {
          bridge.setGlobalVariable(
              "__fbBatchedBridgeConfig",
              buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
          bridge.setGlobalVariable(
              "__RCTProfileIsProfiling",
              Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
        } finally {
          Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
        }
    
        return bridge;
      }
    

    在CatalystInstanceImpl中的实现,其实就是写一个JSON字符串,有两个字段"remoteModuleConfig"和"localModulesConfig",分别对应NativeModule(Native提供给JS调用)和JSModule(JS提供给Native调用)

    private String buildModulesConfigJSONProperty(
          NativeModuleRegistry nativeModuleRegistry,
          JavaScriptModulesConfig jsModulesConfig) {
        JsonFactory jsonFactory = new JsonFactory();
        StringWriter writer = new StringWriter();
        try {
          JsonGenerator jg = jsonFactory.createGenerator(writer);
          jg.writeStartObject();
          jg.writeFieldName("remoteModuleConfig");
          nativeModuleRegistry.writeModuleDescriptions(jg);
          jg.writeFieldName("localModulesConfig");
          jsModulesConfig.writeModuleDescriptions(jg);
          jg.writeEndObject();
          jg.close();
        } catch (IOException ioe) {
          throw new RuntimeException("Unable to serialize JavaScript module declaration", ioe);
        }
        return writer.getBuffer().toString();
      }
    

    再看看localModulesConfig都写入了什么,跟进去JavaScriptModulesConfig.java,就是往JSON字符创中先写入接口名、moduleID、methods(方法名、methodID)等。

    /*package*/ void writeModuleDescriptions(JsonGenerator jg) throws IOException {
        jg.writeStartObject();
        for (JavaScriptModuleRegistration registration : mModules) {
          jg.writeObjectFieldStart(registration.getName());
          appendJSModuleToJSONObject(jg, registration);
          jg.writeEndObject();
        }
        jg.writeEndObject();
      }
    
      private void appendJSModuleToJSONObject(
          JsonGenerator jg,
          JavaScriptModuleRegistration registration) throws IOException {
        jg.writeObjectField("moduleID", registration.getModuleId());
        jg.writeObjectFieldStart("methods");
        for (Method method : registration.getMethods()) {
          jg.writeObjectFieldStart(method.getName());
          jg.writeObjectField("methodID", registration.getMethodId(method));
          jg.writeEndObject();
        }
        jg.writeEndObject();
      }
    

    initializeBridge->setGlobalVariable->buildModulesConfigJSONProperty->writeModuleDescriptions

    整个过程作用是将所有JavaScriptModule的信息生成JSON字符串预先保存到Bridge中, 通过methodID标识作为key存入JSON生成器中,用来最终生成JSON字符串:

    其中setGlobalVariable也是Native函数,buildModulesConfigJSONProperty把所有的JavaScriptModule模块以moduleID+methodID形式生成JSON字符串,通过setGlobalVariable把JSON字符串预先存入了ReactBridge

     bridge.setGlobalVariable(
              "__fbBatchedBridgeConfig",
              buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
    

    Java层的调用就是上面的过程,最后通过JNI调用到C++层,接下来进入这一层看看。

    2.ReactBridge实现

    通信模型图中要调用WebKit的实现,少不了Bridge这个桥梁,因为Java是不能直接调用WebKit,但是可以Java通过JNIJNI再调用WebKit

    JNI入口react/jni/OnLoad.cpp,通过RegisterNatives方式注册的,JNI_OnLoad里面注册了setGlobalVariablecallFunctionnative本地方法

    //jni/react/jni/OnLoad.cpp
    namespace bridge {
      ......
    
    static void setGlobalVariable(JNIEnv* env, jobject obj, jstring propName, jstring jsonValue) {
      auto bridge = extractRefPtr<Bridge>(env, obj);
      bridge->setGlobalVariable(fromJString(env, propName), fromJString(env, jsonValue));
    }
      ...
    } // namespace bridge
    
    extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
      return initialize(vm, [] {
        // get the current env
        JNIEnv* env = Environment::current();
        registerNatives("com/facebook/react/bridge/ReadableNativeMap$ReadableNativeMapKeySetIterator", {
        ......
        registerNatives("com/facebook/react/bridge/ReactBridge", {
            makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create),
            makeNativeMethod(
              "loadScriptFromAssets", "(Landroid/content/res/AssetManager;Ljava/lang/String;)V",
              bridge::loadScriptFromAssets),
            makeNativeMethod("loadScriptFromFile", bridge::loadScriptFromFile),
            makeNativeMethod("callFunction", bridge::callFunction),
            makeNativeMethod("invokeCallback", bridge::invokeCallback),
            makeNativeMethod("setGlobalVariable", bridge::setGlobalVariable),
            makeNativeMethod("supportsProfiling", bridge::supportsProfiling),
            makeNativeMethod("startProfiler", bridge::startProfiler),
            makeNativeMethod("stopProfiler", bridge::stopProfiler),
            makeNativeMethod("handleMemoryPressureModerate", bridge::handleMemoryPressureModerate),
            makeNativeMethod("handleMemoryPressureCritical", bridge::handleMemoryPressureCritical),
    
        });
    }
    

    可以看到setGlobalVariable真正调用的是Bridge.cpp里面的setGlobalVariable:

    //jni/react/Bridge.h
    class JSThreadState;
    class Bridge : public Countable {
    public:
      typedef std::function<void(std::vector<MethodCall>, bool isEndOfBatch)> Callback;
    
      Bridge(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Callback callback);
      virtual ~Bridge();
    
      /**
       * Flush get the next queue of changes.
       */
      void flush();
    
      /**
       * Executes a function with the module ID and method ID and any additional
       * arguments in JS.
       */
      void callFunction(const double moduleId, const double methodId, const folly::dynamic& args);
    
      /**
       * Invokes a callback with the cbID, and optional additional arguments in JS.
       */
      void invokeCallback(const double callbackId, const folly::dynamic& args);
    
      void executeApplicationScript(const std::string& script, const std::string& sourceURL);
      void setGlobalVariable(const std::string& propName, const std::string& jsonValue);
      bool supportsProfiling();
      void startProfiler(const std::string& title);
      void stopProfiler(const std::string& title, const std::string& filename);
      void handleMemoryPressureModerate();
      void handleMemoryPressureCritical();
    private:
      Callback m_callback;
      std::unique_ptr<JSThreadState> m_threadState;
      // This is used to avoid a race condition where a proxyCallback gets queued after ~Bridge(),
      // on the same thread. In that case, the callback will try to run the task on m_callback which
      // will have been destroyed within ~Bridge(), thus causing a SIGSEGV.
      std::shared_ptr<bool> m_destroyed;
    };
    
    void Bridge::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
      m_threadState->setGlobalVariable(propName, jsonValue);
    }
    

    最终调用的是JSThreadState里面的setGlobalVariable:

    //jni/react/Bridge.cpp
    class JSThreadState {
    public:
      JSThreadState(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Bridge::Callback&& callback) :
        m_callback(callback)
      {
        m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON, bool isEndOfBatch) {
          m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
        });
      }
    
      void setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
        m_jsExecutor->setGlobalVariable(propName, jsonValue);
      }
    
    private:
      std::unique_ptr<JSExecutor> m_jsExecutor;
      Bridge::Callback m_callback;
    };
    

    最终调用JSExecutor里面setGlobalVariable:

    //jni/react/JSCExecutor.h
    class JSCExecutor : public JSExecutor, public JSCWebWorkerOwner {
    public:
      /**
       * Should be invoked from the JS thread.
       */
      explicit JSCExecutor(FlushImmediateCallback flushImmediateCallback);
      ~JSCExecutor() override;
    
      virtual void executeApplicationScript(
        const std::string& script,
        const std::string& sourceURL) override;
      virtual std::string flush() override;
      virtual std::string callFunction(
        const double moduleId,
        const double methodId,
        const folly::dynamic& arguments) override;
      virtual std::string invokeCallback(
        const double callbackId,
        const folly::dynamic& arguments) override;
      virtual void setGlobalVariable(
        const std::string& propName,
        const std::string& jsonValue) override;
      virtual bool supportsProfiling() override;
      virtual void startProfiler(const std::string &titleString) override;
      virtual void stopProfiler(const std::string &titleString, const std::string &filename) override;
      virtual void handleMemoryPressureModerate() override;
      virtual void handleMemoryPressureCritical() override;
    
      void flushQueueImmediate(std::string queueJSON);
      void installNativeHook(const char *name, JSObjectCallAsFunctionCallback callback);
      virtual void onMessageReceived(int workerId, const std::string& message) override;
      virtual JSGlobalContextRef getContext() override;
      virtual std::shared_ptr<JMessageQueueThread> getMessageQueueThread() override;
    };
    
    } }
    
    //jni/react/JSCExecutor.cpp
    void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
      auto globalObject = JSContextGetGlobalObject(m_context);
      String jsPropertyName(propName.c_str());
    
      String jsValueJSON(jsonValue.c_str());
      auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);
    
      JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
    }
    

    从JSCExecutor::setGlobalVariable中可以看到java层传过来的JSON串(包含Native Modules和JS Modules)和属性名(__fbBatchedBridgeConfig),赋值给全局变量globalObject,这个全局变量通过JSContextGetGlobalObject获取到,在JSCExecutor.h头文件中:

    #include <memory>
    #include <unordered_map>
    #include <JavaScriptCore/JSContextRef.h>
    #include "Executor.h"
    #include "JSCHelpers.h"
    #include "JSCWebWorker.h"
    

    JavaScriptCore/JSContextRef.h是Webkit这个库中的文件,在ReactAndroid下的build.gradle中有索引这个库,到build.gradle中看看:


    ReactAndroid.png
    //ReactAndroid/build.gradle
    task downloadJSCHeaders(type: Download) {
        def jscAPIBaseURL = 'https://svn.webkit.org/repository/webkit/!svn/bc/174650/trunk/Source/JavaScriptCore/API/'
        def jscHeaderFiles = ['JSBase.h', 'JSContextRef.h', 'JSObjectRef.h', 'JSRetainPtr.h', 'JSStringRef.h', 'JSValueRef.h', 'WebKitAvailability.h']
        def output = new File(downloadsDir, 'jsc')
        output.mkdirs()
        src(jscHeaderFiles.collect { headerName -> "$jscAPIBaseURL$headerName" })
        onlyIfNewer true
        overwrite false
        dest output
    }
    

    打开jscAPIBaseURL这个url可以找到JSContextRef.h头文件:

    //JavaScriptCore/API/JSContextRef.h
    ...
    /*!
    @function
    @abstract Gets the global object of a JavaScript execution context.
    @param ctx The JSContext whose global object you want to get.
    @result ctx's global object.
    */
    JS_EXPORT JSObjectRef JSContextGetGlobalObject(JSContextRef ctx);
    ...
    

    JSCExecutor::setGlobalVariable最后给JS的全局变量设置一个属性__fbBatchedBridgeConfig,这个属性的值就是JavaScriptModule,Java调用JS的时候从属性中查找,看下callFunction:

    //jni/react/JSCExecutor.cpp
    std::string JSCExecutor::callFunction(const double moduleId, const double methodId, const folly::dynamic& arguments) {
      // TODO:  Make this a first class function instead of evaling. #9317773
      std::vector<folly::dynamic> call{
        (double) moduleId,
        (double) methodId,
        std::move(arguments),
      };
      return executeJSCallWithJSC(m_context, "callFunctionReturnFlushedQueue", std::move(call));
    }
    
    static std::string executeJSCallWithJSC(
        JSGlobalContextRef ctx,
        const std::string& methodName,
        const std::vector<folly::dynamic>& arguments) {
      #ifdef WITH_FBSYSTRACE
      FbSystraceSection s(
          TRACE_TAG_REACT_CXX_BRIDGE, "JSCExecutor.executeJSCall",
          "method", methodName);
      #endif
    
      // Evaluate script with JSC
      folly::dynamic jsonArgs(arguments.begin(), arguments.end());
      auto js = folly::to<folly::fbstring>(
          "__fbBatchedBridge.", methodName, ".apply(null, ",
          folly::toJson(jsonArgs), ")");
      auto result = evaluateScript(ctx, String(js.c_str()), nullptr);
      return Value(ctx, result).toJSONString();
    }
    

    最终以

    __fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})的形式拼接一个applyJavascript执行语句,最后调用jni/react/JSCHelpers.cppevaluateScript的来执行这个语句,完成BridgeJavascript的调用,JSCHelpersWebKit的一些API做了封装,它负责最终调用WebKit

    //jni/react/JSCHelpers.h
    namespace facebook {
    namespace react {
    
    void installGlobalFunction(
        JSGlobalContextRef ctx,
        const char* name,
        JSObjectCallAsFunctionCallback callback);
    
    JSValueRef makeJSCException(
        JSContextRef ctx,
        const char* exception_text);
    
    JSValueRef evaluateScript(JSContextRef context, JSStringRef script, JSStringRef source);
    
    } }
    
    

    Bridge层的调用过程: OnLoad.cpp->Bridge.cpp->JSCExecutor.cpp->JSCHelpers.cpp->WebKit

    3.JavaScript层实现

    Javascript的通信,实质上是Weikit执行Javascript语句,调用流程是Bridge->WebKit->JavascriptWebKit中提供了许多与Javascript通信的API,比如evaluateScriptJSContextGetGlobalObjectJSObjectSetProperty等。

    回顾一下前面JSCExecutor::setGlobalVariable:

    //jni/react/JSCExecutor.cpp
    void JSCExecutor::setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
      auto globalObject = JSContextGetGlobalObject(m_context);
      String jsPropertyName(propName.c_str());
    
      String jsValueJSON(jsonValue.c_str());
      auto valueToInject = JSValueMakeFromJSONString(m_context, jsValueJSON);
    
      JSObjectSetProperty(m_context, globalObject, jsPropertyName, valueToInject, 0, NULL);
    }
    

    所有JavaScriptModule信息是调用的setGlobalVariable方法生成一张映射表,这张映射表最终肯定是要保存在Javascript层的,JSContextGetGlobalObjectWeiKit的方法,其目的是获取Global全局对象,jsPropertyName方法字面意思就是Javascript对象的属性名,参数propName是从Java层传递过来的,在CatalystInstanceImpl.java类中可以印证这一点,具体值有两个:__fbBatchedBridgeConfig__RCTProfileIsProfiling.

    //com/facebook/react/bridge/CatalystInstanceImpl
    bridge.setGlobalVariable(
              "__fbBatchedBridgeConfig",
              buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
    

    翻译成JS语句大概是:

    global.__fbBatchedBridgeConfig = jsonValue;
    

    由于__fbBatchedBridgeConfig对象是被直接定义成Global全局对象的属性,就可以直接调用了,类似于window对象,__fbBatchedBridgeConfig对象里又有两个属性:remoteModuleConfiglocalModulesConfig,分别对应Java模块和JS模块。

    到JS层的BatchedBridge.js:

    //Libraries/BatchedBridge/BatchedBridge.js
    const MessageQueue = require('MessageQueue');
    
    const BatchedBridge = new MessageQueue(
      __fbBatchedBridgeConfig.remoteModuleConfig,   //Native(Java)模块
      __fbBatchedBridgeConfig.localModulesConfig,   //JS模块
    );
    
    // TODO: Move these around to solve the cycle in a cleaner way.
    
    const Systrace = require('Systrace');
    const JSTimersExecution = require('JSTimersExecution');
    
    BatchedBridge.registerCallableModule('Systrace', Systrace);
    BatchedBridge.registerCallableModule('JSTimersExecution', JSTimersExecution);
    
    if (__DEV__) {
      BatchedBridge.registerCallableModule('HMRClient', require('HMRClient'));
    }
    
    Object.defineProperty(global, '__fbBatchedBridge', { value: BatchedBridge });
    
    module.exports = BatchedBridge;
    

    1.在全局变量global中定义fbBatchedBridge,在前面JSCExecutor::callFunction中调用JS语句的形式是fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})

    2.BatchedBridge=new MessageQueue

    所以再继续跟到MessageQueue.js中:

    
    class MessageQueue {
    
      constructor(remoteModules, localModules) {
        this.RemoteModules = {};
    
        this._callableModules = {};
        this._queue = [[], [], [], 0];
        this._moduleTable = {};
        this._methodTable = {};
        this._callbacks = [];
        this._callbackID = 0;
        this._callID = 0;
        this._lastFlush = 0;
        this._eventLoopStartTime = new Date().getTime();
    
        [
          'invokeCallbackAndReturnFlushedQueue',
          'callFunctionReturnFlushedQueue',
          'flushedQueue',
        ].forEach((fn) => this[fn] = this[fn].bind(this));
    
        let modulesConfig = this._genModulesConfig(remoteModules);
        this._genModules(modulesConfig);
        localModules && this._genLookupTables(
          this._genModulesConfig(localModules),this._moduleTable, this._methodTable
        );
    
        this._debugInfo = {};
        this._remoteModuleTable = {};
        this._remoteMethodTable = {};
        this._genLookupTables(
          modulesConfig, this._remoteModuleTable, this._remoteMethodTable
        );
      }
    
      /**
       * Public APIs
       */
      callFunctionReturnFlushedQueue(module, method, args) {
        guard(() => {
          this.__callFunction(module, method, args);
          this.__callImmediates();
        });
    
        return this.flushedQueue();
      }
      
      _genLookupTables(modulesConfig, moduleTable, methodTable) {
        modulesConfig.forEach((config, moduleID) => {
          this._genLookup(config, moduleID, moduleTable, methodTable);
        });
      }
    
      _genLookup(config, moduleID, moduleTable, methodTable) {
        if (!config) {
          return;
        }
    
        let moduleName, methods;
        if (moduleHasConstants(config)) {
          [moduleName, , methods] = config;
        } else {
          [moduleName, methods] = config;
        }
    
        moduleTable[moduleID] = moduleName;
        methodTable[moduleID] = Object.assign({}, methods);
      }
    }
    
    module.exports = MessageQueue;
    

    把remoteModules, localModules传进了_genLookupTables的方法里,同时还有两个参数_moduleTable_methodTable,就是我们找的映射表了,一张module映射表,一张method映射表

    再回顾下前面Java调用JS的语句形式:__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null,{moduleId, methodId, arguments})

    
    class MessageQueue {
    
     constructor(remoteModules, localModules) {
       this.RemoteModules = {};
    
       this._callableModules = {};
       this._queue = [[], [], [], 0];
       this._moduleTable = {};
       this._methodTable = {};
       this._callbacks = [];
       this._callbackID = 0;
       this._callID = 0;
       this._lastFlush = 0;
       this._eventLoopStartTime = new Date().getTime();
    
       [
         'invokeCallbackAndReturnFlushedQueue',
         'callFunctionReturnFlushedQueue',
         'flushedQueue',
       ].forEach((fn) => this[fn] = this[fn].bind(this));
    
       let modulesConfig = this._genModulesConfig(remoteModules);
       this._genModules(modulesConfig);
       localModules && this._genLookupTables(
         this._genModulesConfig(localModules),this._moduleTable, this._methodTable
       );
    
       this._debugInfo = {};
       this._remoteModuleTable = {};
       this._remoteMethodTable = {};
       this._genLookupTables(
         modulesConfig, this._remoteModuleTable, this._remoteMethodTable
       );
     }
    
     /**
      * Public APIs
      */
     callFunctionReturnFlushedQueue(module, method, args) {
       guard(() => {
         this.__callFunction(module, method, args);
         this.__callImmediates();
       });
    
       return this.flushedQueue();
     }
     
     _genLookupTables(modulesConfig, moduleTable, methodTable) {
       modulesConfig.forEach((config, moduleID) => {
         this._genLookup(config, moduleID, moduleTable, methodTable);
       });
     }
    
     _genLookup(config, moduleID, moduleTable, methodTable) {
       if (!config) {
         return;
       }
    
       let moduleName, methods;
       if (moduleHasConstants(config)) {
         [moduleName, , methods] = config;
       } else {
         [moduleName, methods] = config;
       }
    
       moduleTable[moduleID] = moduleName;
       methodTable[moduleID] = Object.assign({}, methods);
     }
    }
    
    module.exports = MessageQueue;
    

    其中有个callableModules, 它就是用来存放哪些Javascript组件是可以被调用的,正常情况下callableModules的数据和JavaScriptModules的数据(包括方法名和参数)理应是完全对应的.

    看下哪些JS组件调用registerCallableModule进行方法的注册 resigterCallableModule.png

    看一下RCTEventEmitter.js:

    //Libraries/BatchedBridge/BatchedBridgedModules/RCTEventEmitter.js
    var BatchedBridge = require('BatchedBridge');
    var ReactNativeEventEmitter = require('ReactNativeEventEmitter');
    
    BatchedBridge.registerCallableModule(
      'RCTEventEmitter',
      ReactNativeEventEmitter
    );
    
    // Completely locally implemented - no native hooks.
    module.exports = ReactNativeEventEmitter;
    

    ReactNativeEventEmitter.js:

    var ReactNativeEventEmitter = merge(ReactEventEmitterMixin, {
      receiveEvent: function(
        tag: number,
        topLevelType: string,
        nativeEventParam: Object
      ) {
        ......
      },
    
      receiveTouches: function(
        eventTopLevelType: string,
        touches: Array<Object>,
        changedIndices: Array<number>
        ) {
          ......
        });
    

    对比下Java层的接口表示,方法名称和参数个数是一致的

    public interface RCTEventEmitter extends JavaScriptModule {
      public void receiveEvent(int targetTag, String eventName, @Nullable WritableMap event);
      public void receiveTouches(
          String eventName,
          WritableArray touches,
          WritableArray changedIndices);
    }
    

    继续前面__callFunction的最后一步:moduleMethods[method].apply(moduleMethods, args);调用对应模块的方法。

    4.总结

    Java调用JavaScript可以总结为下面:


    调用流程.png

    欢迎关注公众号:JueCode

    相关文章

      网友评论

      本文标题:React Native通信原理源码分析一

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