ReactNative Android 运行原理之2021-02-07 本文已影响0人
耗子_wo
上一期我们来介绍下ReactNative Android 运行原理之<JAVA TO JavaScript>,这一期我们继续来讲解运行原理之<JavaScript TO JAVA>,按照上期的节奏我们继续是轻松愉快的来帮助大家分析整个运行原理,通过这篇文章帮助大家通俗易懂的去理解,好的,让我们开始吧:首先我们看看JavaScript TO JAVA端的调用过程:
- JavaScript发起通信
比如一个简单的Toast,首先需要引入ToastAndroid模块,然后调用方法显示。
var ToastAndroid = require('ToastAndroid')
ToastAndroid.show('Clicking!', ToastAndroid.SHORT);
//react-native/Libraries/Components/ToastAndroid/ToastAndroid.android.js
'use strict';
var RCTToastAndroid = require('NativeModules').ToastAndroid;
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'
首先JavaScript RN模块想执行一个Native方法比如(ToastAndroid.show())实际调用的是RN里面的NativeModules.js中的ToastAndroid模块 RCTToastAndroid.show(message, duration),一开始在JAVA层就有注册了这个模块的对象例如:(public class ToastModule extends ReactContextBaseJavaModule),
@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();
}
});
}
...
}
我们先看看Javascript端是怎么知道JAVA端的模块的呢:
-
首先NativeModules.js其中const RemoteModules = BatchedBridge.RemoteModules就是Java层注册的供JS层调用的模块(这个可参考上一篇文章提到的BatchedBridge通过JavascriptCore注入一开始就初始化好了)
-
接着会循环遍历RemoteModules将Module放入变量NativeModules中(里面就包括ToastAndroid模块)
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;
},
});
});
- 遍历会调用BatchedBridge.processModuleConfig(config, module.moduleID)方法里面最后调用_genMethod方法,这个里面会把对应module的所有相关方法都绑定到self.__nativeCall(module, method, args, resolve, (errorData)这个方法里面来(意思就是说module[methodName] = self.__nativeCall)
//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;
}
module[methodName] = self.__nativeCall 意思就是把所有对于JAVA模块的调用都抽象成了一个统一的函数处理,这种思路我们在ReactNative IOS端的源码中
也发现了这种处理的方式
我们再来看看__nativeCall函数会干什么,在if判断里面如果两次调用时间间隔大于5ms,就会调用global.nativeFlushQueueImmediate(this._queue),其中this._queue就是数组,包含:MODULE_IDS、METHOD_IDS、PARAMS、_callID,global.nativeFlushQueueImmediate方法是在C++层进行注入的,到JSCExecutor.cpp中,通过installGlobalFunction注入函数。
//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) + ')');
}
}
这里不得不再次说道JavascriptCore这个东西,JavaScript最终执行是在JavascriptCore里面,JavascriptCore又在C++层,通过WebKit(或者说JavascriptCore Api)暴露的接口方法我们可以让JavaScript与C++层代码传递数据和方法(方法理解成函数指针其实也是数据)
//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);
}
接下来C++ 层处理的步骤比较复杂,我们化繁为简把基础步骤列出来即可,让大家先通俗易懂的把大致过程先理解,如果有疑问细节问题可以留言咨询我
最终会调用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:
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 */);
});
}
}
JSThreadState 又是怎么创立的呢:是在构造Bridge的时候创建的
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)));
}
那么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));
}
里面 auto bridge = createNew<Bridge>(nativeExecutorFactory, bridgeCallback); 就是创建了一个Bridge,并且把bridgeCallback作为callback传递进去,所以真正要运行的"callback" 就是:dispatchCallbacksToJava
auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector<MethodCall> calls, bool isEndOfBatch) {
dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls), isEndOfBatch);
};
函数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");
...
}
dispatchCallbacksToJava 会调用 makeJavaCall,makeJavaCall里面会触发JNI的反射调用 env->CallVoidMethod(callback, gCallbackMethod, call.moduleId, call.methodId, newArray.get());
其中gCallbackMethod是通过GetMethodID找到的方法,其中callbackClass就是com/facebook/react/bridge/ReactCallback,调用这个类的call方法,有三个参数:int,int, com/facebook/react/bridge/ReadableNativeArray
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(IILcom/facebook/react/bridge/ReadableNativeArray;)V");
意思就是"callback"就是要调用JAVA类的ReactCallback的call方法,一句话描述就这么简单:
@DoNotStrip
public interface ReactCallback {
@DoNotStrip
void call(int moduleId, int methodId, ReadableNativeArray parameters);
@DoNotStrip
void onBatchComplete();
}
让我们回到JAVA层再来看看:其中Bridge的初始化在CatalystInstanceImpl.java中:可以看到接口ReactCallback的实现类是NativeModulesReactCallback,是CatalystInstanceImpl的内部类:
//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);
}
···
//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);
}
mJavaRegistry.call(CatalystInstanceImpl.this, moduleId, methodId, parameters); 这样就调用到了NativeModulesReactCallback的call函数中
//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);
}
}
}
先看看他的getMethods方法是怎么实现的:其实很简单就是通过反射拿到类对象的所有方法,然后查询是否有@ReactMethod 注解的方法然后保存起来在一个Map里面
public abstract class BaseJavaModule implements NativeModule {
static final private ArgumentExtractor<Callback> ARGUMENT_EXTRACTOR_CALLBACK = new ArgumentExtractor<Callback>() {
@Override
public @Nullable
Callback extractArgument(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
if (jsArguments.isNull(atIndex)) {
return null;
} else {
int id = (int) jsArguments.getDouble(atIndex); //CallbackImpl 实现 Callback接口
return new CallbackImpl(catalystInstance, executorToken, id);
}
}
};
static final private ArgumentExtractor<Promise> ARGUMENT_EXTRACTOR_PROMISE = new ArgumentExtractor<Promise>() {
@Override
public int getJSArgumentsNeeded() {
return 2;
}
@Override
public Promise extractArgument(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
Callback resolve = ARGUMENT_EXTRACTOR_CALLBACK.extractArgument(catalystInstance, executorToken, jsArguments, atIndex);
Callback reject = ARGUMENT_EXTRACTOR_CALLBACK.extractArgument(catalystInstance, executorToken, jsArguments, atIndex + 1);
return new PromiseImpl(resolve, reject);
}
};
private @Nullable
Map<String, NativeMethod> mMethods;
private @Nullable
Map<String, SyncNativeHook> mHooks; // getMethods实现
@Override
public final Map<String, NativeMethod> getMethods() {
Method[] targetMethods = getClass().getDeclaredMethods();
for (Method targetMethod : targetMethods) {
if (targetMethod.getAnnotation(ReactMethod.class) != null) {
String methodName = targetMethod.getName();
mMethods.put(methodName, new JavaMethod(targetMethod));
}
if (targetMethod.getAnnotation(ReactSyncHook.class) != null) {
String methodName = targetMethod.getName();
mHooks.put(methodName, new SyncJavaHook(targetMethod));
}
}
return assertNotNull(mMethods);
}
public class JavaMethod implements NativeMethod {
private Method mMethod;
public JavaMethod(Method method) {
mMethod = method;
}
@Override
public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters) {
mMethod.invoke(BaseJavaModule.this, mArguments);
}
}
}
public abstract class ReactContextBaseJavaModule extends BaseJavaModule {
}
public class IntentModule extends ReactContextBaseJavaModule {
}
其中ModuleDefinition是NativeModuleRegistry的静态私有内部类,也是每个NativeModule的一层包装,在上面的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 的invoke方法:
//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);
}
}
我现在来几句话总结一遍这个C++ 到 JAVA的真个调用过程:
-
查找m_callback方法找到了dispatchCallbacksToJava方法里面makeJavaCall方法,里面需要调用方法 env->CallVoidMethod(callback, gCallbackMethod, call.moduleId, call.methodId, newArray.get()),
-
然后到gCallbackMethod方法(JNI方法准备回到JAVA层),最终callback在Java层就是ReactCallback,ReactCallback的实现类是NativeModulesReactCallback,是CatalystInstanceImpl的内部类,然后调用到mJavaRegistry.call方法中,
-
根据ModuleId找到了对应的ModuleDefinition,然后调用它的call方法(ModuleDefinition是NativeModuleRegistry的静态私有内部类,也是每个NativeModule的一层包装,意思就是说找到了对应的NativeModule)
-
最终调用到了ModuleDefinition里面的MethodRegistration的method.invoke方法(ModuleDefinition初始化的时候会把相关Module的所有方法加一个List里面),最后通过反射mMethod.invoke(BaseJavaModule.this, mArguments)调用到BaseJavaModule的mMethod方法(BaseJavaModule就是业务层定义的的NativeModule)
新版本的invoke变成了下面这样更加好理解一些:
try {
mMethod.invoke(mModuleWrapper.getModule(), mArguments);
}
好了,我们已经介绍完,不知道这样讲解你是否能够通俗易懂的去理解呢,如果有疑问的话欢迎给我留言···
上一期我们来介绍下ReactNative Android 运行原理之<JAVA TO JavaScript>,这一期我们继续来讲解运行原理之<JavaScript TO JAVA>,按照上期的节奏我们继续是轻松愉快的来帮助大家分析整个运行原理,通过这篇文章帮助大家通俗易懂的去理解,好的,让我们开始吧:首先我们看看JavaScript TO JAVA端的调用过程:
- JavaScript发起通信
比如一个简单的Toast,首先需要引入ToastAndroid模块,然后调用方法显示。
var ToastAndroid = require('ToastAndroid')
ToastAndroid.show('Clicking!', ToastAndroid.SHORT);
//react-native/Libraries/Components/ToastAndroid/ToastAndroid.android.js
'use strict';
var RCTToastAndroid = require('NativeModules').ToastAndroid;
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'
首先JavaScript RN模块想执行一个Native方法比如(ToastAndroid.show())实际调用的是RN里面的NativeModules.js中的ToastAndroid模块 RCTToastAndroid.show(message, duration),一开始在JAVA层就有注册了这个模块的对象例如:(public class ToastModule extends ReactContextBaseJavaModule),
@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();
}
});
}
...
}
我们先看看Javascript端是怎么知道JAVA端的模块的呢:
-
首先NativeModules.js其中const RemoteModules = BatchedBridge.RemoteModules就是Java层注册的供JS层调用的模块(这个可参考上一篇文章提到的BatchedBridge通过JavascriptCore注入一开始就初始化好了)
-
接着会循环遍历RemoteModules将Module放入变量NativeModules中(里面就包括ToastAndroid模块)
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;
},
});
});
- 遍历会调用BatchedBridge.processModuleConfig(config, module.moduleID)方法里面最后调用_genMethod方法,这个里面会把对应module的所有相关方法都绑定到self.__nativeCall(module, method, args, resolve, (errorData)这个方法里面来(意思就是说module[methodName] = self.__nativeCall)
//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;
}
module[methodName] = self.__nativeCall 意思就是把所有对于JAVA模块的调用都抽象成了一个统一的函数处理,这种思路我们在ReactNative IOS端的源码中
也发现了这种处理的方式
我们再来看看__nativeCall函数会干什么,在if判断里面如果两次调用时间间隔大于5ms,就会调用global.nativeFlushQueueImmediate(this._queue),其中this._queue就是数组,包含:MODULE_IDS、METHOD_IDS、PARAMS、_callID,global.nativeFlushQueueImmediate方法是在C++层进行注入的,到JSCExecutor.cpp中,通过installGlobalFunction注入函数。
//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) + ')');
}
}
这里不得不再次说道JavascriptCore这个东西,JavaScript最终执行是在JavascriptCore里面,JavascriptCore又在C++层,通过WebKit(或者说JavascriptCore Api)暴露的接口方法我们可以让JavaScript与C++层代码传递数据和方法(方法理解成函数指针其实也是数据)
//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);
}
接下来C++ 层处理的步骤比较复杂,我们化繁为简把基础步骤列出来即可,让大家先通俗易懂的把大致过程先理解,如果有疑问细节问题可以留言咨询我
最终会调用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:
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 */);
});
}
}
JSThreadState 又是怎么创立的呢:是在构造Bridge的时候创建的
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)));
}
那么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));
}
里面 auto bridge = createNew<Bridge>(nativeExecutorFactory, bridgeCallback); 就是创建了一个Bridge,并且把bridgeCallback作为callback传递进去,所以真正要运行的"callback" 就是:dispatchCallbacksToJava
auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector<MethodCall> calls, bool isEndOfBatch) {
dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls), isEndOfBatch);
};
函数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");
...
}
dispatchCallbacksToJava 会调用 makeJavaCall,makeJavaCall里面会触发JNI的反射调用 env->CallVoidMethod(callback, gCallbackMethod, call.moduleId, call.methodId, newArray.get());
其中gCallbackMethod是通过GetMethodID找到的方法,其中callbackClass就是com/facebook/react/bridge/ReactCallback,调用这个类的call方法,有三个参数:int,int, com/facebook/react/bridge/ReadableNativeArray
jclass callbackClass = env->FindClass("com/facebook/react/bridge/ReactCallback");
bridge::gCallbackMethod = env->GetMethodID(callbackClass, "call", "(IILcom/facebook/react/bridge/ReadableNativeArray;)V");
意思就是"callback"就是要调用JAVA类的ReactCallback的call方法,一句话描述就这么简单:
@DoNotStrip
public interface ReactCallback {
@DoNotStrip
void call(int moduleId, int methodId, ReadableNativeArray parameters);
@DoNotStrip
void onBatchComplete();
}
让我们回到JAVA层再来看看:其中Bridge的初始化在CatalystInstanceImpl.java中:可以看到接口ReactCallback的实现类是NativeModulesReactCallback,是CatalystInstanceImpl的内部类:
//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);
}
···
//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);
}
mJavaRegistry.call(CatalystInstanceImpl.this, moduleId, methodId, parameters); 这样就调用到了NativeModulesReactCallback的call函数中
//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);
}
}
}
先看看他的getMethods方法是怎么实现的:其实很简单就是通过反射拿到类对象的所有方法,然后查询是否有@ReactMethod 注解的方法然后保存起来在一个Map里面
public abstract class BaseJavaModule implements NativeModule {
static final private ArgumentExtractor<Callback> ARGUMENT_EXTRACTOR_CALLBACK = new ArgumentExtractor<Callback>() {
@Override
public @Nullable
Callback extractArgument(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
if (jsArguments.isNull(atIndex)) {
return null;
} else {
int id = (int) jsArguments.getDouble(atIndex); //CallbackImpl 实现 Callback接口
return new CallbackImpl(catalystInstance, executorToken, id);
}
}
};
static final private ArgumentExtractor<Promise> ARGUMENT_EXTRACTOR_PROMISE = new ArgumentExtractor<Promise>() {
@Override
public int getJSArgumentsNeeded() {
return 2;
}
@Override
public Promise extractArgument(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray jsArguments, int atIndex) {
Callback resolve = ARGUMENT_EXTRACTOR_CALLBACK.extractArgument(catalystInstance, executorToken, jsArguments, atIndex);
Callback reject = ARGUMENT_EXTRACTOR_CALLBACK.extractArgument(catalystInstance, executorToken, jsArguments, atIndex + 1);
return new PromiseImpl(resolve, reject);
}
};
private @Nullable
Map<String, NativeMethod> mMethods;
private @Nullable
Map<String, SyncNativeHook> mHooks; // getMethods实现
@Override
public final Map<String, NativeMethod> getMethods() {
Method[] targetMethods = getClass().getDeclaredMethods();
for (Method targetMethod : targetMethods) {
if (targetMethod.getAnnotation(ReactMethod.class) != null) {
String methodName = targetMethod.getName();
mMethods.put(methodName, new JavaMethod(targetMethod));
}
if (targetMethod.getAnnotation(ReactSyncHook.class) != null) {
String methodName = targetMethod.getName();
mHooks.put(methodName, new SyncJavaHook(targetMethod));
}
}
return assertNotNull(mMethods);
}
public class JavaMethod implements NativeMethod {
private Method mMethod;
public JavaMethod(Method method) {
mMethod = method;
}
@Override
public void invoke(CatalystInstance catalystInstance, ExecutorToken executorToken, ReadableNativeArray parameters) {
mMethod.invoke(BaseJavaModule.this, mArguments);
}
}
}
public abstract class ReactContextBaseJavaModule extends BaseJavaModule {
}
public class IntentModule extends ReactContextBaseJavaModule {
}
其中ModuleDefinition是NativeModuleRegistry的静态私有内部类,也是每个NativeModule的一层包装,在上面的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 的invoke方法:
//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);
}
}
我现在来几句话总结一遍这个C++ 到 JAVA的真个调用过程:
-
查找m_callback方法找到了dispatchCallbacksToJava方法里面makeJavaCall方法,里面需要调用方法 env->CallVoidMethod(callback, gCallbackMethod, call.moduleId, call.methodId, newArray.get()),
-
然后到gCallbackMethod方法(JNI方法准备回到JAVA层),最终callback在Java层就是ReactCallback,ReactCallback的实现类是NativeModulesReactCallback,是CatalystInstanceImpl的内部类,然后调用到mJavaRegistry.call方法中,
-
根据ModuleId找到了对应的ModuleDefinition,然后调用它的call方法(ModuleDefinition是NativeModuleRegistry的静态私有内部类,也是每个NativeModule的一层包装,意思就是说找到了对应的NativeModule)
-
最终调用到了ModuleDefinition里面的MethodRegistration的method.invoke方法(ModuleDefinition初始化的时候会把相关Module的所有方法加一个List里面),最后通过反射mMethod.invoke(BaseJavaModule.this, mArguments)调用到BaseJavaModule的mMethod方法(BaseJavaModule就是业务层定义的的NativeModule)
新版本的invoke变成了下面这样更加好理解一些:
try {
mMethod.invoke(mModuleWrapper.getModule(), mArguments);
}
好了,我们已经介绍完,不知道这样讲解你是否能够通俗易懂的去理解呢,如果有疑问的话欢迎给我留言···