React Native通信原理源码分析二

作者: juexingzhe | 来源:发表于2018-04-16 22:46 被阅读94次

    本篇文章已授权微信公众号 JueCode 独家发布

    上一篇中分析了Native调用JavaScript的原理,这一篇我们分析下JavaScript调用Native的原理。上一篇提到的内容这里就不重复了,建议小伙伴们先看下上一篇再来看这一篇,会更好理解点。

    1.JavaScript发起通信

    比如一个简单的Toast,首先需要引入ToastAndroid模块,然后调用方法显示。

    var ToastAndroid = require('ToastAndroid')
    ToastAndroid.show('Clicking!', ToastAndroid.SHORT);
    

    这个Toast是需要Native来显示的,在Android上就是需要Java层处理的,那么是怎么传递过去的?

    首先先看下模块ToastAndroid,在ToastAndroid.android.js文件中,可以看出来是引入NativeModules中的ToastAndroid:

    //react-native/Libraries/Components/ToastAndroid/ToastAndroid.android.js
    'use strict';
    
    var RCTToastAndroid = require('NativeModules').ToastAndroid;
    
    /**
     * This exposes the native ToastAndroid module as a JS module. This has a function 'show'
     * which takes the following parameters:
     *
     * 1. String message: A string with the text to toast
     * 2. int duration: The duration of the toast. May be ToastAndroid.SHORT or ToastAndroid.LONG
     */
    
    var ToastAndroid = {
    
      SHORT: RCTToastAndroid.SHORT,
      LONG: RCTToastAndroid.LONG,
    
      show: function (
        message: string,
        duration: number
      ): void {
        RCTToastAndroid.show(message, duration);
      },
    
    };
    
    module.exports = ToastAndroid;
    

    实际调用的是NativeModules中的ToastAndroid模块,先看下Java注册本地模块,在getName方法中返回模块的名字'ToastAndroid'

    @ReactModule(name = "ToastAndroid")
    public class ToastModule extends ReactContextBaseJavaModule {
    
      private static final String DURATION_SHORT_KEY = "SHORT";
      private static final String DURATION_LONG_KEY = "LONG";
    
      private static final String GRAVITY_TOP_KEY = "TOP";
      private static final String GRAVITY_BOTTOM_KEY = "BOTTOM";
      private static final String GRAVITY_CENTER = "CENTER";
    
      public ToastModule(ReactApplicationContext reactContext) {
        super(reactContext);
      }
    
      @Override
      public String getName() {
        return "ToastAndroid";
      }
    
      @Override
      public Map<String, Object> getConstants() {
        final Map<String, Object> constants = MapBuilder.newHashMap();
        constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
        constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
        constants.put(GRAVITY_TOP_KEY, Gravity.TOP | Gravity.CENTER_HORIZONTAL);
        constants.put(GRAVITY_BOTTOM_KEY, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
        constants.put(GRAVITY_CENTER, Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
        return constants;
      }
    
      @ReactMethod
      public void show(final String message, final int duration) {
        UiThreadUtil.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            Toast.makeText(getReactApplicationContext(), message, duration).show();
          }
        });
      }
     ...
    }
    

    其实有看过上一篇的小伙伴应该就能猜出来,ToastAndroid这个模块其实就是Native传递到JS层的,接着往下看。

    接着到NativeModules.js中, 其中RemoteModules就是Java层注册的供JS层调用的模块,这个可以参考上一篇:React Native源码分析之Native调用JavaScript.接着会循环遍历RemoteModules将Module放入变量NativeModules中,在上面中就可以找到ToastAndroid这个模块:

    //react-native/Libraries/BatchedBridge/BatchedBridgedModules/NativeModules.js
    'use strict';
    
    const BatchedBridge = require('BatchedBridge');
    const RemoteModules = BatchedBridge.RemoteModules;
    
    function normalizePrefix(moduleName: string): string {
      return moduleName.replace(/^(RCT|RK)/, '');
    }
    
    /**
     * Dirty hack to support old (RK) and new (RCT) native module name conventions.
     */
    Object.keys(RemoteModules).forEach((moduleName) => {
      const strippedName = normalizePrefix(moduleName);
      if (RemoteModules['RCT' + strippedName] && RemoteModules['RK' + strippedName]) {
        throw new Error(
          'Module cannot be registered as both RCT and RK: ' + moduleName
        );
      }
      if (strippedName !== moduleName) {
        RemoteModules[strippedName] = RemoteModules[moduleName];
        delete RemoteModules[moduleName];
      }
    });
    
    /**
     * Define lazy getters for each module.
     * These will return the module if already loaded, or load it if not.
     */
    const NativeModules = {};
    Object.keys(RemoteModules).forEach((moduleName) => {
      Object.defineProperty(NativeModules, moduleName, {
        enumerable: true,
        get: () => {
          let module = RemoteModules[moduleName];
          if (module && typeof module.moduleID === 'number' && global.nativeRequireModuleConfig) {
            const json = global.nativeRequireModuleConfig(moduleName);
            const config = json && JSON.parse(json);
            module = config && BatchedBridge.processModuleConfig(config, module.moduleID);
            RemoteModules[moduleName] = module;
          }
          return module;
        },
      });
    });
    ...
    module.exports = NativeModules;
    

    在上面遍历RemoteModules会调用BatchedBridge.processModuleConfig(config, module.moduleID);接着往下看:

    //react-native/Libraries/Utilities/MessageQueue.js
    processModuleConfig(config, moduleID) {
        const module = this._genModule(config, moduleID);
        this._genLookup(config, moduleID, this._remoteModuleTable, this._remoteMethodTable);
        return module;
      }
    
    _genModule(config, moduleID) {
        if (!config) {
          return;
        }
    
        let moduleName, constants, methods, asyncMethods;
        if (moduleHasConstants(config)) {
          [moduleName, constants, methods, asyncMethods] = config;
        } else {
          [moduleName, methods, asyncMethods] = config;
        }
    
        let module = {};
        methods && methods.forEach((methodName, methodID) => {
          const methodType =
            asyncMethods && arrayContains(asyncMethods, methodID) ?
              MethodTypes.remoteAsync : MethodTypes.remote;
          module[methodName] = this._genMethod(moduleID, methodID, methodType);
        });
        Object.assign(module, constants);
    
        if (!constants && !methods && !asyncMethods) {
          module.moduleID = moduleID;
        }
    
        this.RemoteModules[moduleName] = module;
        return module;
      }
    
    _genMethod(module, method, type) {
        let fn = null;
        let self = this;
        if (type === MethodTypes.remoteAsync) {
          fn = function(...args) {
            return new Promise((resolve, reject) => {
              self.__nativeCall(module, method, args, resolve, (errorData) => {
                var error = createErrorFromErrorData(errorData);
                reject(error);
              });
            });
          };
        } else {
          fn = function(...args) {
            let lastArg = args.length > 0 ? args[args.length - 1] : null;
            let secondLastArg = args.length > 1 ? args[args.length - 2] : null;
            let hasSuccCB = typeof lastArg === 'function';
            let hasErrorCB = typeof secondLastArg === 'function';
            hasErrorCB && invariant(
              hasSuccCB,
              'Cannot have a non-function arg after a function arg.'
            );
            let numCBs = hasSuccCB + hasErrorCB;
            let onSucc = hasSuccCB ? lastArg : null;
            let onFail = hasErrorCB ? secondLastArg : null;
            args = args.slice(0, args.length - numCBs);
            return self.__nativeCall(module, method, args, onFail, onSucc);
          };
        }
        fn.type = type;
        return fn;
      }
    

    最终ToastAndroid.show会调用self.__nativeCall,接着看下这个函数,在if判断里面如果两次调用时间间隔大于5ms,就会调用global.nativeFlushQueueImmediate(this._queue),其中this._queue就是数组,包含:MODULE_IDS、METHOD_IDS、PARAMS、_callID

    //react-native/Libraries/Utilities/MessageQueue.js
    let MIN_TIME_BETWEEN_FLUSHES_MS = 5;
    __nativeCall(module, method, params, onFail, onSucc) {
        if (onFail || onSucc) {
          // eventually delete old debug info
          (this._callbackID > (1 << 5)) &&
            (this._debugInfo[this._callbackID >> 5] = null);
    
          this._debugInfo[this._callbackID >> 1] = [module, method];
          onFail && params.push(this._callbackID);
          this._callbacks[this._callbackID++] = onFail;
          onSucc && params.push(this._callbackID);
          this._callbacks[this._callbackID++] = onSucc;
        }
    
        global.nativeTraceBeginAsyncFlow &&
          global.nativeTraceBeginAsyncFlow(TRACE_TAG_REACT_APPS, 'native', this._callID);
        this._callID++;
    
        this._queue[MODULE_IDS].push(module);
        this._queue[METHOD_IDS].push(method);
        this._queue[PARAMS].push(params);
    
        var now = new Date().getTime();
        if (global.nativeFlushQueueImmediate &&
            now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
          global.nativeFlushQueueImmediate(this._queue);
          this._queue = [[], [], [], this._callID];
          this._lastFlush = now;
        }
        Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length);
        if (__DEV__ && SPY_MODE && isFinite(module)) {
          console.log('JS->N : ' + this._remoteModuleTable[module] + '.' +
            this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')');
        }
      }
    

    这个跟Java层的处理逻辑有点类似,统一收拢调用逻辑,Java调用JS层模块最后都通过JavaScriptModuleRegistry中动态代理来统一处理;JS层调用Java模块就统一收拢逻辑到MessageQueue.js中的__nativeCall函数中。那么上面的global.nativeFlushQueueImmediate(this._queue);方法在那注入到global中的?猜测应该是C++层,接着往下看。

    2.C++注册与传递

    global.nativeFlushQueueImmediate方法是在C++层进行注入的,到JSCExecutor.cpp中,通过installGlobalFunction注入函数。

    //react-native/ReactAndroid/src/main/jni/react/JSCExecutor.cpp
    JSCExecutor::JSCExecutor(FlushImmediateCallback cb) :
        m_flushImmediateCallback(cb) {
      m_context = JSGlobalContextCreateInGroup(nullptr, nullptr);
      m_messageQueueThread = JMessageQueueThread::currentMessageQueueThread();
      s_globalContextRefToJSCExecutor[m_context] = this;
      installGlobalFunction(m_context, "nativeFlushQueueImmediate", nativeFlushQueueImmediate);
      installGlobalFunction(m_context, "nativeLoggingHook", nativeLoggingHook);
      installGlobalFunction(m_context, "nativePerformanceNow", nativePerformanceNow);
      installGlobalFunction(m_context, "nativeStartWorker", nativeStartWorker);
      installGlobalFunction(m_context, "nativePostMessageToWorker", nativePostMessageToWorker);
      installGlobalFunction(m_context, "nativeTerminateWorker", nativeTerminateWorker);
    
      #ifdef WITH_FB_JSC_TUNING
      configureJSCForAndroid();
      #endif
    
      #ifdef WITH_JSC_EXTRA_TRACING
      addNativeTracingHooks(m_context);
      addNativeProfilingHooks(m_context);
      addNativePerfLoggingHooks(m_context);
      #endif
    
      #ifdef WITH_FB_MEMORY_PROFILING
      addNativeMemoryHooks(m_context);
      #endif
    }
    
    // Native JS hooks
    static JSValueRef nativeFlushQueueImmediate(
        JSContextRef ctx,
        JSObjectRef function,
        JSObjectRef thisObject,
        size_t argumentCount,
        const JSValueRef arguments[],
        JSValueRef *exception) {
      if (argumentCount != 1) {
        *exception = createErrorString(ctx, "Got wrong number of args");
        return JSValueMakeUndefined(ctx);
      }
    
      JSCExecutor *executor;
      try {
        executor = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
      } catch (std::out_of_range& e) {
        *exception = createErrorString(ctx, "Global JS context didn't map to a valid executor");
        return JSValueMakeUndefined(ctx);
      }
    
      std::string resStr = Value(ctx, arguments[0]).toJSONString();
    
      executor->flushQueueImmediate(resStr);
    
      return JSValueMakeUndefined(ctx);
    }
    
    void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
      m_flushImmediateCallback(queueJSON, false);
    }
    

    最终会调用m_flushImmediateCallback(queueJSON, false),其中m_flushImmediateCallback是在构造JSCExecutor中传入:

    //react-native/ReactAndroid/src/main/jni/react/JSCExecutor.cpp
    JSCExecutor::JSCExecutor(FlushImmediateCallback cb) : m_flushImmediateCallback(cb)
    

    在头文件JSCExecutor.h中,其中JSCExecutor会通过JSCExecutorFactory进行构造:

    //react-native/ReactAndroid/src/main/jni/react/JSCExecutor.h
    class JSCExecutorFactory : public JSExecutorFactory {
    public:
      virtual std::unique_ptr<JSExecutor> createJSExecutor(FlushImmediateCallback cb) override;
    };
    
    class JSCExecutor : public JSExecutor, public JSCWebWorkerOwner {
    public:
      /**
       * Should be invoked from the JS thread.
       */
      explicit JSCExecutor(FlushImmediateCallback flushImmediateCallback);
      ~JSCExecutor() override;
      ...
    }
    

    在Bridge.cpp中可以看到,也就是最终调用的都是构造Bridge时候传入的Callback

    //react-native/ReactAndroid/src/main/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 */);
        });
      }
    }
    
    Bridge::Bridge(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Callback callback) :
      m_callback(callback),
      m_destroyed(std::shared_ptr<bool>(new bool(false)))
    {
      auto destroyed = m_destroyed;
      auto proxyCallback = [this, destroyed] (std::vector<MethodCall> calls, bool isEndOfBatch) {
        if (*destroyed) {
          return;
        }
        m_callback(std::move(calls), isEndOfBatch);
      };
      m_threadState.reset(new JSThreadState(jsExecutorFactory, std::move(proxyCallback)));
    }
    

    其中m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);中parseMethodCalls如下,其实就是把前面js层的this._queue解析到vector中

    //react-native/ReactAndroid/src/main/jni/react/MethodCall.cpp
    std::vector<MethodCall> parseMethodCalls(const std::string& json) {
      folly::dynamic jsonData = folly::parseJson(json);
    
      if (jsonData.isNull()) {
        return {};
      }
    
      if (!jsonData.isArray()) {
        jni::throwNewJavaException(jni::gJavaLangIllegalArgumentException,
                                   "Did not get valid calls back from JS: %s", jsonData.typeName());
      }
    
      if (jsonData.size() < REQUEST_PARAMSS + 1) {
        jni::throwNewJavaException(jni::gJavaLangIllegalArgumentException,
                                   "Did not get valid calls back from JS: size == %d", jsonData.size());
      }
    
      auto moduleIds = jsonData[REQUEST_MODULE_IDS];
      auto methodIds = jsonData[REQUEST_METHOD_IDS];
      auto params = jsonData[REQUEST_PARAMSS];
      int  callId = -1;
    
      if (!moduleIds.isArray() || !methodIds.isArray() || !params.isArray()) {
        jni::throwNewJavaException(jni::gJavaLangIllegalArgumentException,
                                   "Did not get valid calls back from JS: %s",
                                   json.c_str());
      }
    
      if (jsonData.size() > REQUEST_CALLID) {
        if (!jsonData[REQUEST_CALLID].isInt()) {
          jni::throwNewJavaException(jni::gJavaLangIllegalArgumentException,
                                   "Did not get valid calls back from JS: %s",
                                   json.c_str());
        } else {
          callId = jsonData[REQUEST_CALLID].getInt();
        }
      }
    
      std::vector<MethodCall> methodCalls;
      for (size_t i = 0; i < moduleIds.size(); i++) {
        auto paramsValue = params[i];
        if (!paramsValue.isArray()) {
          jni::throwNewJavaException(jni::gJavaLangIllegalArgumentException,
                                     "Call argument isn't an array");
        }
    
        methodCalls.emplace_back(
          moduleIds[i].getInt(),
          methodIds[i].getInt(),
          std::move(params[i]),
          callId);
    
        // only incremement callid if contains valid callid as callid is optional
        callId += (callId != -1) ? 1 : 0;
      }
    
      return methodCalls;
    }
    
    struct MethodCall {
      int moduleId;
      int methodId;
      folly::dynamic arguments;
      int callId;
    
      MethodCall(int mod, int meth, folly::dynamic args, int cid)
        : moduleId(mod)
        , methodId(meth)
        , arguments(std::move(args))
        , callId(cid) {}
    };
    

    那么Bridge是在哪边构造?在OnLoad.cpp中,在create函数中构造bridge,create函数通过registerNatives动态注册,看到这里就知道是Java层会通过JNI调用来初始化bridge

    //react-native/ReactAndroid/src/main/jni/react/OnLoad.cpp
    static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback,
                       jobject callbackQueueThread) {
      auto weakCallback = createNew<WeakReference>(callback);
      auto weakCallbackQueueThread = createNew<WeakReference>(callbackQueueThread);
      auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector<MethodCall> calls, bool isEndOfBatch) {
        dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls), isEndOfBatch);
      };
      auto nativeExecutorFactory = extractRefPtr<JSExecutorFactory>(env, executor);
      auto bridge = createNew<Bridge>(nativeExecutorFactory, bridgeCallback);
      setCountableForJava(env, obj, std::move(bridge));
    }
    
    extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
      return initialize(vm, [] {
        // get the current env
        JNIEnv* env = Environment::current();
    
        auto readableTypeClass = findClassLocal("com/facebook/react/bridge/ReadableType");
        type::gReadableReactType = (jclass)env->NewGlobalRef(readableTypeClass.get());
        type::initialize(env);
    
        NativeArray::registerNatives();
        ReadableNativeArray::registerNatives();
        WritableNativeArray::registerNatives();
        JNativeRunnable::registerNatives();
        registerJSLoaderNatives();
    
        registerNatives("com/facebook/react/bridge/JSCJavaScriptExecutor", {
          makeNativeMethod("initialize", executors::createJSCExecutor),
        });
    
        registerNatives("com/facebook/react/bridge/ProxyJavaScriptExecutor", {
            makeNativeMethod(
              "initialize", "(Lcom/facebook/react/bridge/JavaJSExecutor;)V",
              executors::createProxyExecutor),
        });
    
        jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
        bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(IILcom/facebook/react/bridge/ReadableNativeArray;)V");
        bridge::gOnBatchCompleteMethod = env->GetMethodID(callbackClass, "onBatchComplete", "()V");
    
        jclass markerClass = env->FindClass("com/facebook/react/bridge/ReactMarker");
        bridge::gLogMarkerMethod = env->GetStaticMethodID(markerClass, "logMarker", "(Ljava/lang/String;)V");
    
        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),
    
        });
    

    其中有个函数dispatchCallbacksToJava,看名字就知道会通过这个回调Java, 其中for (auto&& call : calls) 说明JS调用Java可以发起多个调用,一次发送给Java层处理

    //react-native/ReactAndroid/src/main/jni/react/OnLoad.cpp
    static void dispatchCallbacksToJava(const RefPtr<WeakReference>& weakCallback,
                                        const RefPtr<WeakReference>& weakCallbackQueueThread,
                                        std::vector<MethodCall>&& calls,
                                        bool isEndOfBatch) {
      auto env = Environment::current();
      if (env->ExceptionCheck()) {
        FBLOGW("Dropped calls because of pending exception");
        return;
      }
    
      ResolvedWeakReference callbackQueueThread(weakCallbackQueueThread);
      if (!callbackQueueThread) {
        FBLOGW("Dropped calls because of callback queue thread went away");
        return;
      }
    
      auto runnableFunction = std::bind([weakCallback, isEndOfBatch] (std::vector<MethodCall>& calls) {
        auto env = Environment::current();
        if (env->ExceptionCheck()) {
          FBLOGW("Dropped calls because of pending exception");
          return;
        }
        ResolvedWeakReference callback(weakCallback);
        if (callback) {
          for (auto&& call : calls) {
            makeJavaCall(env, callback, std::move(call));
            if (env->ExceptionCheck()) {
              return;
            }
          }
          if (isEndOfBatch) {
            signalBatchComplete(env, callback);
          }
        }
      }, std::move(calls));
    
      auto jNativeRunnable = runnable::createNativeRunnable(env, std::move(runnableFunction));
      queue::enqueueNativeRunnableOnQueue(env, callbackQueueThread, jNativeRunnable.get());
    }
    
    static void makeJavaCall(JNIEnv* env, jobject callback, MethodCall&& call) {
      if (call.arguments.isNull()) {
        return;
      }
    
      #ifdef WITH_FBSYSTRACE
      if (call.callId != -1) {
        fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", call.callId);
      }
      #endif
    
      auto newArray = ReadableNativeArray::newObjectCxxArgs(std::move(call.arguments));
      env->CallVoidMethod(callback, gCallbackMethod, call.moduleId, call.methodId, newArray.get());
    }
    
    static jmethodID gCallbackMethod;
    
    extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
      return initialize(vm, [] {
        // get the current env
        JNIEnv* env = Environment::current();
    
        auto readableTypeClass = findClassLocal("com/facebook/react/bridge/ReadableType");
        type::gReadableReactType = (jclass)env->NewGlobalRef(readableTypeClass.get());
        type::initialize(env);
    
        NativeArray::registerNatives();
        ReadableNativeArray::registerNatives();
        WritableNativeArray::registerNatives();
        JNativeRunnable::registerNatives();
        registerJSLoaderNatives();
    
        ...
        jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
        bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(IILcom/facebook/react/bridge/ReadableNativeArray;)V");
        ...
    }
    
    

    到上面C++就通过JNI回调到Java层了,接着往下看。

    3.Java层处理

    在上面C++层怎么找到Java对应的方法?

    gCallbackMethod是通过GetMethodID找到的方法,其中callbackClass就是com/facebook/react/bridge/ReactCallback,调用这个类的call方法,有三个参数:

    int,int, com/facebook/react/bridge/ReadableNativeArray

    对比看下C++和Java:

    //react-native/ReactAndroid/src/main/jni/react/OnLoad.cpp        
    makeNativeMethod("initialize", "(Lcom/facebook/react/bridge/JavaScriptExecutor;Lcom/facebook/react/bridge/ReactCallback;Lcom/facebook/react/bridge/queue/MessageQueueThread;)V", bridge::create),
    
    
    public ReactBridge(
          JavaScriptExecutor jsExecutor,
          ReactCallback callback,
          MessageQueueThread nativeModulesQueueThread) {
        mJSExecutor = jsExecutor;
        mCallback = callback;
        mNativeModulesQueueThread = nativeModulesQueueThread;
        initialize(jsExecutor, callback, mNativeModulesQueueThread);
      }
    
    private native void initialize(
          JavaScriptExecutor jsExecutor,
          ReactCallback callback,
          MessageQueueThread nativeModulesQueueThread);
    

    最终callback在Java层就是ReactCallback:

    @DoNotStrip
    public interface ReactCallback {
    
      @DoNotStrip
      void call(int moduleId, int methodId, ReadableNativeArray parameters);
    
      @DoNotStrip
      void onBatchComplete();
    }
    

    其中Bridge的初始化在CatalystInstanceImpl.java中:

    //com/facebook/react/bridge/CatalystInstanceImpl.java
    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;
      }
    

    可以看到接口ReactCallback的实现类是NativeModulesReactCallback,是CatalystInstanceImpl的内部类:

    //com/facebook/react/bridge/CatalystInstanceImpl.java
    private class NativeModulesReactCallback implements ReactCallback {
    
        @Override
        public void call(int moduleId, int methodId, ReadableNativeArray parameters) {
          mCatalystQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
    
          // Suppress any callbacks if destroyed - will only lead to sadness.
          if (mDestroyed) {
            return;
          }
    
          mJavaRegistry.call(CatalystInstanceImpl.this, moduleId, methodId, parameters);
        }
    
        @Override
        public void onBatchComplete() {
          mCatalystQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
    
          // The bridge may have been destroyed due to an exception during the batch. In that case
          // native modules could be in a bad state so we don't want to call anything on them. We
          // still want to trigger the debug listener since it allows instrumentation tests to end and
          // check their assertions without waiting for a timeout.
          if (!mDestroyed) {
            Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onBatchComplete");
            try {
              mJavaRegistry.onBatchComplete();
            } finally {
              Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
            }
          }
    
          decrementPendingJSCalls();
        }
      }
    

    调用到NativeModuleRegistry中,其中ModuleDefinition是NativeModuleRegistry的静态私有内部类,也是每个NativeModule的一层包装:

    //com/facebook/react/bridge/NativeModuleRegistry.java
    void call(
          CatalystInstance catalystInstance,
          int moduleId,
          int methodId,
          ReadableNativeArray parameters) {
        ModuleDefinition definition = mModuleTable.get(moduleId);
        if (definition == null) {
          throw new RuntimeException("Call to unknown module: " + moduleId);
        }
        definition.call(catalystInstance, methodId, parameters);
      }
    
    public static class Builder {
    
        private final HashMap<String, NativeModule> mModules = MapBuilder.newHashMap();
    
        public Builder add(NativeModule module) {
          NativeModule existing = mModules.get(module.getName());
          if (existing != null && !module.canOverrideExistingModule()) {
            throw new IllegalStateException("Native module " + module.getClass().getSimpleName() +
                " tried to override " + existing.getClass().getSimpleName() + " for module name " +
                module.getName() + ". If this was your intention, return true from " +
                module.getClass().getSimpleName() + "#canOverrideExistingModule()");
          }
          mModules.put(module.getName(), module);
          return this;
        }
    
        public NativeModuleRegistry build() {
          List<ModuleDefinition> moduleTable = new ArrayList<>();
          Map<Class<? extends NativeModule>, NativeModule> moduleInstances = new HashMap<>();
    
          int idx = 0;
          for (NativeModule module : mModules.values()) {
            ModuleDefinition moduleDef = new ModuleDefinition(idx++, module.getName(), module);
            moduleTable.add(moduleDef);
            moduleInstances.put(module.getClass(), module);
          }
          return new NativeModuleRegistry(moduleTable, moduleInstances);
        }
      }
    
    private static class ModuleDefinition {
        public final int id;
        public final String name;
        public final NativeModule target;
        public final ArrayList<MethodRegistration> methods;
    
        public ModuleDefinition(int id, String name, NativeModule target) {
          this.id = id;
          this.name = name;
          this.target = target;
          this.methods = new ArrayList<MethodRegistration>();
    
          for (Map.Entry<String, NativeModule.NativeMethod> entry : target.getMethods().entrySet()) {
            this.methods.add(
              new MethodRegistration(
                entry.getKey(), "NativeCall__" + target.getName() + "_" + entry.getKey(),
                entry.getValue()));
          }
        }
    
        public void call(
            CatalystInstance catalystInstance,
            int methodId,
            ReadableNativeArray parameters) {
          MethodRegistration method = this.methods.get(methodId);
          Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, method.tracingName);
          try {
            this.methods.get(methodId).method.invoke(catalystInstance, parameters);
          } finally {
            Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
          }
        }
      }
    

    在上面的ModuleDefinition中call方法最终调用MethodRegistration的method.invoke:

    //com/facebook/react/bridge/NativeModuleRegistry.java
    private static class MethodRegistration {
        public MethodRegistration(String name, String tracingName, NativeModule.NativeMethod method) {
          this.name = name;
          this.tracingName = tracingName;
          this.method = method;
        }
    
        public String name;
        public String tracingName;
        public NativeModule.NativeMethod method;
      }
    

    再调用到NativeModule.NativeMethod,是NativeModule的内部接口:

    //com/facebook/react/bridge/NativeModule.java
    public interface NativeModule {
      interface NativeMethod {
        void invoke(CatalystInstance catalystInstance, ReadableNativeArray parameters);
        String getType();
      }
    

    实现类是JavaMethod,是BaseJavaModule的私有内部类, 最后通过反射mMethod.invoke(BaseJavaModule.this, mArguments);调用目标BaseJavaModule的方法。

    //com/facebook/react/bridge/BaseJavaModule.java
    private class JavaMethod implements NativeMethod {
    
        private Method mMethod;
        private final ArgumentExtractor[] mArgumentExtractors;
        private final Object[] mArguments;
        private String mType = METHOD_TYPE_REMOTE;
        private final int mJSArgumentsNeeded;
    
        public JavaMethod(Method method) {
          mMethod = method;
          Class[] parameterTypes = method.getParameterTypes();
          mArgumentExtractors = buildArgumentExtractors(parameterTypes);
          // Since native methods are invoked from a message queue executed on a single thread, it is
          // save to allocate only one arguments object per method that can be reused across calls
          mArguments = new Object[parameterTypes.length];
          mJSArgumentsNeeded = calculateJSArgumentsNeeded();
        }
    
        private ArgumentExtractor[] buildArgumentExtractors(Class[] paramTypes) {
          ArgumentExtractor[] argumentExtractors = new ArgumentExtractor[paramTypes.length];
          for (int i = 0; i < paramTypes.length; i += argumentExtractors[i].getJSArgumentsNeeded()) {
            Class argumentClass = paramTypes[i];
            if (argumentClass == Boolean.class || argumentClass == boolean.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_BOOLEAN;
            } else if (argumentClass == Integer.class || argumentClass == int.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_INTEGER;
            } else if (argumentClass == Double.class || argumentClass == double.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_DOUBLE;
            } else if (argumentClass == Float.class || argumentClass == float.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_FLOAT;
            } else if (argumentClass == String.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_STRING;
            } else if (argumentClass == Callback.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_CALLBACK;
            } else if (argumentClass == Promise.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_PROMISE;
              Assertions.assertCondition(
                  i == paramTypes.length - 1, "Promise must be used as last parameter only");
              mType = METHOD_TYPE_REMOTE_ASYNC;
            } else if (argumentClass == ReadableMap.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_MAP;
            } else if (argumentClass == ReadableArray.class) {
              argumentExtractors[i] = ARGUMENT_EXTRACTOR_ARRAY;
            } else {
              throw new RuntimeException(
                  "Got unknown argument class: " + argumentClass.getSimpleName());
            }
          }
          return argumentExtractors;
        }
      
        ...
    
        @Override
        public void invoke(CatalystInstance catalystInstance, ReadableNativeArray parameters) {
          Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "callJavaModuleMethod");
          try {
            if (mJSArgumentsNeeded != parameters.size()) {
              throw new NativeArgumentsParseException(
                  BaseJavaModule.this.getName() + "." + mMethod.getName() + " got " +
                  parameters.size() + " arguments, expected " + mJSArgumentsNeeded);
            }
    
            int i = 0, jsArgumentsConsumed = 0;
            try {
              for (; i < mArgumentExtractors.length; i++) {
                mArguments[i] = mArgumentExtractors[i].extractArgument(
                    catalystInstance, parameters, jsArgumentsConsumed);
                jsArgumentsConsumed += mArgumentExtractors[i].getJSArgumentsNeeded();
              }
            } catch (UnexpectedNativeTypeException e) {
              throw new NativeArgumentsParseException(
                  e.getMessage() + " (constructing arguments for " + BaseJavaModule.this.getName() +
                  "." + mMethod.getName() + " at argument index " +
                  getAffectedRange(jsArgumentsConsumed, mArgumentExtractors[i].getJSArgumentsNeeded()) +
                  ")",
                  e);
            }
    
            try {
              mMethod.invoke(BaseJavaModule.this, mArguments);
            } catch (IllegalArgumentException ie) {
              throw new RuntimeException(
                  "Could not invoke " + BaseJavaModule.this.getName() + "." + mMethod.getName(), ie);
            } catch (IllegalAccessException iae) {
              throw new RuntimeException(
                  "Could not invoke " + BaseJavaModule.this.getName() + "." + mMethod.getName(), iae);
            } catch (InvocationTargetException ite) {
              // Exceptions thrown from native module calls end up wrapped in InvocationTargetException
              // which just make traces harder to read and bump out useful information
              if (ite.getCause() instanceof RuntimeException) {
                throw (RuntimeException) ite.getCause();
              }
              throw new RuntimeException(
                  "Could not invoke " + BaseJavaModule.this.getName() + "." + mMethod.getName(), ite);
            }
          } finally {
            Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
          }
        }
    

    其中ArgumentExtractor是参数提取器,有下面几种:

    //com/facebook/react/bridge/BaseJavaModule.java
    ARGUMENT_EXTRACTOR_BOOLEAN
    ARGUMENT_EXTRACTOR_DOUBLE
    ARGUMENT_EXTRACTOR_FLOAT
    ARGUMENT_EXTRACTOR_INTEGER
    ARGUMENT_EXTRACTOR_STRING
    ARGUMENT_EXTRACTOR_ARRAY
    ARGUMENT_EXTRACTOR_MAP
    ARGUMENT_EXTRACTOR_CALLBACK
    ARGUMENT_EXTRACTOR_PROMISE
    

    到这里就完成了JavaScript调用Java模块的过程分析了。

    4.总结

    JavaScript调用Java模块的过程比它的反过程简单一点,整个流程总结:


    JavaScript调用Java.png

    相关文章

      网友评论

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

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