本篇文章已授權微信公眾號 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模組的過程比它的反過程簡單一點,整個流程總結:
歡迎關注公眾號 JueCode