React Native通訊原理原始碼分析二

juexingzhe發表於2018-04-16

本篇文章已授權微信公眾號 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

歡迎關注公眾號 JueCode

相關文章