react-native.js 其实就是声明了ReactNative提供的可以在js中使用的各种模块。
var ReactNative = Object.assign(Object.create(require('React')), {
...
ToastAndroid: require('ToastAndroid'),
});
module.exports = ReactNative;
ToastAndroid.android.js show方法其实就是调用了RCTToastAndroid.show(message, duration)
var RCTToastAndroid = require('NativeModules').ToastAndroid;
var ToastAndroid = {
SHORT: RCTToastAndroid.SHORT,
LONG: RCTToastAndroid.LONG,
show: function (
message: string,
duration: number
): void {
RCTToastAndroid.show(message, duration);
},
};
NativeModules.js RCTToastAndroid是定义在NativeModules中的
var NativeModules = require('BatchedBridge').RemoteModules;
var nativeModulePrefixNormalizer = require('nativeModulePrefixNormalizer');
nativeModulePrefixNormalizer(NativeModules);
module.exports = NativeModules;
BatchedBridge.js 最终,所有的模块都是来自BatchedBridge,它做的事情就是构造一个MessageQueue对象。
let MessageQueue = require('MessageQueue');
let BatchedBridge = new MessageQueue(
__fbBatchedBridgeConfig.remoteModuleConfig,
__fbBatchedBridgeConfig.localModulesConfig,
);
module.exports = BatchedBridge;
ToastAndroid.show方法其实最终调用的是MessageQueue.ToastAndroid.show方法。
__fbBatchedBridgeConfig 是一个全局js变量,它是在CatalystInstance.java中声明赋值的,通过调用ReactBridge.setGlobalVariable方法。setGlobalVariable是在Jni中声明的方法,最终会调用JavaScriptCore,把Java中定义的JSON字符串,赋值给js的全局对象__fbBatchedBridgeConfig,这个对象会有两个属性remoteModuleConfig和localModulesConfig。
__fbBatchedBridgeConfig.remoteModuleConfig,
__fbBatchedBridgeConfig.localModulesConfig,
CatalystInstanceImpl.java
private native void initializeBridge(
ReactCallback callback,
JavaScriptExecutor jsExecutor,
MessageQueueThread jsQueue,
MessageQueueThread moduleQueue,
Collection javaModules,
Collection cxxModules);
private void initializeBridge(
JavaScriptExecutor jsExecutor,
NativeModuleRegistry registry,
JavaScriptModulesConfig jsModulesConfig,
JSBundleLoader jsBundleLoader) {
mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
mBridge = new ReactBridge(
jsExecutor,
new NativeModulesReactCallback(),
mCatalystQueueConfiguration.getNativeModulesQueueThread());
mBridge.setGlobalVariable(
"__fbBatchedBridgeConfig",
buildModulesConfigJSONProperty(registry, jsModulesConfig));
jsBundleLoader.loadScript(mBridge);
}
__fbBatchedBridgeConfig.remoteModuleConfig 代表的是Java中定义的一些模块,这些模块可以在js中被调用。格式如下:
{
"remoteModuleConfig": {
"Logger": {
"constants": { /* If we had exported constants... */ },
"moduleID": 1,
"methods": {
"requestPermissions": {
"type": "remote",
"methodID": 1
}
}
}
}
}
{
'ToastAndroid': {
moduleId: 0,
methods: {
'show': {
methodID: 0
}
},
constants: {
'SHORT': '0',
'LONG': '1'
}
},
'moduleB': {
moduleId: 0,
methods: {
'method1': {
methodID: 0
}
},
'key1': 'value1',
'key2': 'value2'
}
}
MessageQueue.js 构造函数中首先定义了一些实例变量,注释里面的js module指的是只在js中定义的模块,native module指的是在native(这里就是Java)层定义的模块,这些模块都可以在js中使用。
this.RemoteModules = {};//存储最终生成的各个模块信息,包含模块名,模块中的方法,常量等信息
this._require = customRequire || require;//用于加载模块的函数
this._queue = [[],[],[]];//队列,用于存放调用的模块,方法和参数信息,分别存储在第一二三个数组中
this._moduleTable = {};//moduleId查找moduleName的map,用于js module
this._methodTable = {};//methodId查找methodName的map,用于js module
this._callbacks = [];//回调函数数组,和queue一一对应,每个queue中调用的方法,如果有回调函数,那么就在这个数组的对应坐标上
this._callbackID = 0;//回调函数的id,自增
this._genModules(remoteModules);
localModules && this._genLookupTables(
localModules, this._moduleTable, this._methodTable);
this._debugInfo = {};//放置一些debug相关的信息,主要是调用模块,函数,参数的信息
this._remoteModuleTable = {};//moduleId查找moduleName的map,用于native module
this._remoteMethodTable = {};//methodId查找methodName的map,用于native module
this._genLookupTables(
remoteModules, this._remoteModuleTable, this._remoteMethodTable);
_genModules(remoteModules) 遍历传过来的remoteModules所有的key,得到moduleName,然后针对每个module调用_genModule方法
_genModules(remoteModules) {
let moduleNames = Object.keys(remoteModules);
for (var i = 0, l = moduleNames.length; i < l; i++) {
let moduleName = moduleNames[i];
let moduleConfig = remoteModules[moduleName];
this.RemoteModules[moduleName] = this._genModule({}, moduleConfig);
}
}
_genModule(module, moduleConfig) _genModule方法和_genModules方法类似,遍历module下面的所有的方法,对每个方法,调用_genMethod方法。
_genModule(module, moduleConfig) {
let methodNames = Object.keys(moduleConfig.methods);
for (var i = 0, l = methodNames.length; i < l; i++) {
let methodName = methodNames[i];
let methodConfig = moduleConfig.methods[methodName];
module[methodName] = this._genMethod(
moduleConfig.moduleID, methodConfig.methodID, methodConfig.type);
}
Object.assign(module, moduleConfig.constants);
return module;
}
_genMethod(module, method, type) {
...
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);
};
}
__nativeCall(module, method, params, onFail, onSucc)
- 方法首先检查是否有onFail和onSucc,如果有的话就压入_callbacks栈中,同时把_callbackID存入参数中。
- 接着可以看到,_queue其实被当做了三个栈来使用,分别压入模块名,方法名和参数信息。
- 到这里MessageQueue的构造函数就分析的差不多了,那么我们最开始的ToastAndroid.show(message, duration);方法调用,其实就是往_queue栈中压入了一些信息而已,那么最终是怎么在Java层调用到Android原生的Toast模块的呢?
__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;
}
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._lastFlush = now;
}
if (__DEV__ && SPY_MODE && isFinite(module)) {
console.log('JS->N : ' + this._remoteModuleTable[module] + '.' +
this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')');
}
}
- 这里的关键就是__nativeCall中调用的nativeFlushQueueImmediate方法,这个方法其实C++代码中注入到Js的一个全局变量,具体怎么注入的,就是在JSCExecutor.cpp中调用installGlobalFunction,installGlobalFunction的是通过JavaScriptCore的API来实现让Js可以调用C++代码的。
- nativeFlushQueueImmediate其实又调用了JSCExecutor.cpp中的flushQueueImmediate方法。
- 其中,m_flushImmediateCallback是在JSCExecutor的构造函数中初始化。
- 那么JSCExecutor对象又是在哪了被创建出来的呢?RN中是通过JSCExecutorFactory这个工厂的createJSExecutor方法来创建JSCExecutor对象的,而这个方法的实现刚好就在JSCExecutor.cpp中。
JSCExecutor.cpp
JSCExecutor::JSCExecutor(FlushImmediateCallback cb) :
m_flushImmediateCallback(cb) {}
installGlobalFunction(m_context, "nativeFlushQueueImmediate", nativeFlushQueueImmediate);
installGlobalFunction(m_context, "nativeLoggingHook", nativeLoggingHook);
installGlobalFunction(m_context, "nativePerformanceNow", nativePerformanceNow);
void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
m_flushImmediateCallback(queueJSON);
}
std::unique_ptr JSCExecutorFactory::createJSExecutor(FlushImmediateCallback cb) {
return std::unique_ptr(new JSCExecutor(cb));
}
- 接下来问题又来了,又是谁调用了JSCExecutorFactory.createJSExecutor呢?答案就是Bridge.cpp,
- Bridge.cpp是RN的Jni层的入口,Java层大部分调用的Jni函数都是在这个文件中定义的。
Bridge.cpp
Bridge::Bridge(const RefPtr& jsExecutorFactory, Callback callback) :
m_threadState.reset(new JSThreadState(jsExecutorFactory, std::move(proxyCallback)));
JSThreadState(const RefPtr& jsExecutorFactory, Bridge::Callback&& callback) :
m_callback(callback)
{
m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON) {
m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
});
}
ReactBridge.java回去调用jni中注册的initialize方法。RN所有jni中注册的方法,都在OnLoad.cpp。
OnLoad.cpp 可以看到initialize方法其实就是OnLoad.cpp中的bridge这个namespace下得create方法
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),
});
static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback,
jobject callbackQueueThread) {
auto weakCallback = createNew(callback);
auto weakCallbackQueueThread = createNew(callbackQueueThread);
auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector calls, bool isEndOfBatch) {
dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls), isEndOfBatch);
};
auto nativeExecutorFactory = extractRefPtr(env, executor);
auto bridge = createNew(nativeExecutorFactory, bridgeCallback);
setCountableForJava(env, obj, std::move(bridge));
}
这里调用了Bridge类的构造函数,而Bridge的构造函数中,回去构造一个JSThreadState对象,Bridge所有的API调用都会委托给这个创建的JSThreadState对象。
Bridge::Bridge(const RefPtr& jsExecutorFactory, Callback callback) :
m_callback(callback),
m_destroyed(std::shared_ptr(new bool(false)))
{
auto destroyed = m_destroyed;
auto proxyCallback = [this, destroyed] (std::vector calls, bool isEndOfBatch) {
if (*destroyed) {
return;
}
m_callback(std::move(calls), isEndOfBatch);
};
m_threadState.reset(new JSThreadState(jsExecutorFactory, std::move(proxyCallback)));
}
- JSThreadState的构造函数中,会去创建一个JSCExecutor类的对象
- 这里createJSExecutor方法中,传递的是一个C++的函数,这个函数会被赋值给m_flushImmediateCallback成员变量。
JSThreadState(const RefPtr& jsExecutorFactory, Bridge::Callback&& callback) :
m_callback(callback)
{
m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON) {
m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
});
}
找到了创建JSCExecutor对象的地方了,回到刚才,我们说,所有的Js调用Native Module的API的时候,都会调用flushQueueImmediate方法,而flushQueueImmediate方法中会去调用m_flushImmediateCallback函数。
JSCExecutor::JSCExecutor(FlushImmediateCallback cb) :
m_flushImmediateCallback(cb) {
std::unique_ptr JSCExecutorFactory::createJSExecutor(FlushImmediateCallback cb) {
return std::unique_ptr(new JSCExecutor(cb));
}
也就是说,Js层所有的API调用,都会走到这个m_flushImmediateCallback函数的调用中,这个函数是实现Js和Native通讯的核心。而这里的m_flushImmediateCallback在CPP层,最终是在OnLoad.cpp的bridge::create方法中传入的,这个create方法又是由Java层来调用的,Bridge.java的构造函数中会调用这个create方法,所以说,Js层调用Native API的时候,最终就是调用了Bridge.java中传递过来的ReactCallback对象
public ReactBridge(
JavaScriptExecutor jsExecutor,
ReactCallback callback,
MessageQueueThread nativeModulesQueueThread) {
.....
}
- ReactBridge对象的创建,是在CatalystInstanceImpl.java中
- 这里传递的是ReactCallback类的子类NativeModulesReactCallback,最终调用的是call方法,而call方法又调用了mJavaRegistry.call方法。看到mJavaRegistry,开发过RN应用的同学应该感到很熟悉了,对的,这里就是对应我们在应用启动的时候注册的NativeModule对象。
private ReactBridge initializeBridge
bridge = new ReactBridge(
jsExecutor,
new NativeModulesReactCallback(),
mCatalystQueueConfiguration.getNativeModulesQueueThread());
private class NativeModulesReactCallback implements ReactCallback {
public void call(int moduleId, int methodId, ReadableNativeArray parameters) {
mJavaRegistry.call(CatalystInstanceImpl.this, moduleId, methodId, parameters);
}
}
NativeModuleRegistry.Java
/* package */ 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);
}
我们在RN中注册NativeModule是通过add一个ReactPackage对象来实现,家下来我们看一下,RN是如何把我们注册的各个module添加到NativeModuleRegistry中的。
mReactInstanceManager = ReactInstanceManager.builder()
.addPackage(new MainReactPackage())
.build();
ReactInstanceManager.Builder的build方法,会new一个ReactInstanceManagerImpl对象,把我们的ReactPackage对象传递过去。ReactInstanceManagerImpl类的核心就是createReactContext方法,createReactContext会首先遍历所有注册的ReactPackage,对所有的NativeModule,构造一个ModuleDefinition对象,保存到nativeModuleRegistry对象的mModuleTable中。最后,createReactContext会通过CatalystInstanceImpl.Builder构造一个CatalystInstance对象,并把包含各个NativeModule信息的nativeModuleRegistry对象传递过去。具体的模块的注册过程,我后面再写一篇单独的博客介绍,此处就不啰嗦了。
private ReactApplicationContext createReactContext(JavaScriptExecutor jsExecutor, JSBundleLoader jsBundleLoader) {
...
for (ReactPackage reactPackage : mPackages) {
processPackage(reactPackage, reactContext, nativeRegistryBuilder, jsModulesBuilder);
}
...
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setCatalystQueueConfigurationSpec(CatalystQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)
.setJSModulesConfig(javaScriptModulesConfig)
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
}
回到我们最开始的例子中,我们要在Js中使用Toast,那么我们的MainReactPackage中,就需要构造一个ToastModule,来注册给RN,这样才可以给Js调用。
public List createNativeModules(ReactApplicationContext reactContext) {
return Arrays.asList(
new ToastModule(reactContext));
}
需要注意的是,所有要给Js中使用的模块,都需要在Native这边注册一个对应的Module
在Js中调用ToastAndroid这个模块的流程就是,Js调用会调用到C++中m_flushImmediateCallback函数,参数就是Js函数的参数加上调用的模块名构成的一个JSON字符串。C++中,有一个parseMethodCalls方法,会从Js传递的JSON中,解析出moduleName,functionName,参数等一系列信息,然后C++层会调用Java层的ReactCallback类,Java代码中,会根据传递来的moduleName,functionName找到对应的模块中的方法,然后通过反射执行这些方法,并把参数传递过去。这样就完成了Js对Native代码的调用。