此文基于react natve的 September 2018 - revision 5 版本java
在个人上一篇文章《带你完全看懂React Native和Android原生控件之间的映射关系》中,我已经完整地剖析了从RN组件到原生控件之间的映射关系,文中简单地提到了一些通讯原理,本文我就来详细地讲解一下RN的通讯原理。react
PS:网上讲解RN通讯原理的相关文章不少,但参差不齐,有的代码泛滥,逻辑混乱,有的过于简单,结构不清,有的代码严重过期,已不是当前RN主流版本的源码。本文的目的就是想让读者对最新的RN通讯原理有一个清晰的认识。Let's get started!android
RN通讯原理简单地讲就是,一方将其部分方法注册成一个映射表,另外一方再在这个映射表中查找并调用相应的方法,而jsBridge担当二者间桥接的角色。 git
按原理简述中的顺序,我将本节分红两部分,一是从native(java)出发的注册过程,二是从js出发的调用过程,中间还穿插了部分jsBridge中的C++内容。github
先看官方教程中的例子:react-native
public class ToastModule extends ReactContextBaseJavaModule {
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "ToastExample";
}
@ReactMethod
public void show(String message, int duration) {
// 能够被js调用的方法
Toast.makeText(getReactApplicationContext(), message, duration).show();
}
}
复制代码
这个例子我稍稍简化了一下,功能很简单,就是注册了一个原生模块(NativeModule
)供js调用后弹Toast。promise
public class CustomToastPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new ToastModule(reactContext));// 被添加到ReactPackage中
return modules;
}
}
复制代码
// YourActivity.java
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.addPackage(new CustomToastPackage()) // 传入ReactInstanceManager中
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build()
复制代码
以上的代码都是官方教程中的代码。bash
由上面的代码可见NativeModule
被添加到了ReactPackage
中并被传入了ReactInstanceManager
中。写过RN的人对ReactInstanceManager
确定不会陌生,写RN所在的Activity时必然会实例化ReactInstanceManager
,RN在Android端几乎全部的通讯逻辑都在它内部完成。app
接下来开始源码的分析:异步
// ReactInstanceManager.java
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
CatalystInstanceImpl.Builder catalystInstanceBuilder = new CatalystInstanceImpl.Builder()
.setReactQueueConfigurationSpec(ReactQueueConfigurationSpec.createDefault())
.setJSExecutor(jsExecutor)
.setRegistry(nativeModuleRegistry)// NativeModuleRegistry 会在CatalystInstanceImpl中被调用
.setJSBundleLoader(jsBundleLoader)
.setNativeModuleCallExceptionHandler(exceptionHandler);
private NativeModuleRegistry processPackages(
ReactApplicationContext reactContext,
List<ReactPackage> packages,
boolean checkAndUpdatePackageMembership) {
NativeModuleRegistryBuilder nativeModuleRegistryBuilder = new NativeModuleRegistryBuilder(reactContext, this);
...
for (ReactPackage reactPackage : packages) {
// ReactPackage都传入了NativeModuleRegistry
processPackage(reactPackage, nativeModuleRegistryBuilder);
}
...
NativeModuleRegistry nativeModuleRegistry;
nativeModuleRegistry = nativeModuleRegistryBuilder.build();
...
return nativeModuleRegistry;
}
private void processPackage(
ReactPackage reactPackage,
NativeModuleRegistryBuilder nativeModuleRegistryBuilder) {
...
nativeModuleRegistryBuilder.processPackage(reactPackage);
...
}
复制代码
以上是ReactInstanceManager
中的部分代码,能够看到,ReactPackage
会被传入NativeModuleRegistry
中,NativeModuleRegistry
内部就是一张映射表,全部注册的NativeModule
都会保存在它内部供外部调用。而NativeModuleRegistry
会在CatalystInstanceImpl
中被调用。
看看CatalystInstanceImpl
内部逻辑:
public class CatalystInstanceImpl implements CatalystInstance {
static {
// 初始化jni
ReactBridge.staticInit();
}
private CatalystInstanceImpl(
final ReactQueueConfigurationSpec reactQueueConfigurationSpec,
final JavaScriptExecutor jsExecutor,
final NativeModuleRegistry nativeModuleRegistry,
final JSBundleLoader jsBundleLoader,
NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
...
mNativeModuleRegistry = nativeModuleRegistry;
// 将原生模块注册表传给jsBridge
initializeBridge(
new BridgeCallback(this),
jsExecutor,
mReactQueueConfiguration.getJSQueueThread(),
mNativeModulesQueueThread,
mNativeModuleRegistry.getJavaModules(this),
mNativeModuleRegistry.getCxxModules());
...
}
// C++中执行的方法
private native void initializeBridge(
ReactCallback callback,
JavaScriptExecutor jsExecutor,
MessageQueueThread jsQueue,
MessageQueueThread moduleQueue,
Collection<JavaModuleWrapper> javaModules,
Collection<ModuleHolder> cxxModules);
...
}
复制代码
可见CatalystInstanceImpl
已经和jsBridge(即ReactBridge
)联系在一块儿了,它用C++函数initializeBridge
将原生模块映射表传到jsBridge中。
再看看CatalystInstanceImpl
在C++中的实现:
// CatalystInstanceImpl.cpp
void CatalystInstanceImpl::initializeBridge(
jni::alias_ref<ReactCallback::javaobject> callback,
JavaScriptExecutorHolder* jseh,
jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
jni::alias_ref<JavaMessageQueueThread::javaobject> nativeModulesQueue,
jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {
...
// 将原生模块映射表传给ModuleRegistry.cpp
moduleRegistry_ = std::make_shared<ModuleRegistry>(
buildNativeModuleList(
std::weak_ptr<Instance>(instance_),
javaModules,
cxxModules,
moduleMessageQueue_));
...
}
复制代码
接下来是ModuleRegistry
:
// ModuleRegistry.cpp
ModuleRegistry::ModuleRegistry(std::vector<std::unique_ptr<NativeModule>> modules, ModuleNotFoundCallback callback)
: modules_{std::move(modules)}, moduleNotFoundCallback_{callback} {}
...
void ModuleRegistry::callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId) {
...
// 原生模块注册表被调用处1
modules_[moduleId]->invoke(methodId, std::move(params), callId);
}
MethodCallResult ModuleRegistry::callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params) {
...
// 原生模块注册表被调用处2
return modules_[moduleId]->callSerializableNativeHook(methodId, std::move(params));
}
复制代码
可见ModuleRegistry
是原生模块映射表在C++中的位置,ModuleRegistry
中暴露出了函数callNativeMethod
供js调用。
原生模块的注册过程就这样分析完毕了。
咱们先看官方文档中的调用原生模块的方法:
import {NativeModules} from 'react-native';
NativeModules.ToastExample.show('Awesome', 1);
复制代码
这样就调用了原生的Toast。它主要调用了NativeModules.js
,ToastExample
是ToastModule
的getName
方法返回的字符串,而show
是ToastModule
中加了ReactMethod
注解的方法。
接下来看看ReactMethod
的源码:
function genModule(
config: ?ModuleConfig,
moduleID: number,
): ?{name: string, module?: Object} {
const [moduleName, constants, methods, promiseMethods, syncMethods] = config;
...
// 获取原生方法
module[methodName] = genMethod(moduleID, methodID, methodType);
...
return {name: moduleName, module};
}
function genMethod(moduleID: number, methodID: number, type: MethodType) {
let fn = null;
if (type === 'promise') {
// 异步调用
fn = function(...args: Array<any>) {
return new Promise((resolve, reject) => {
BatchedBridge.enqueueNativeCall(
moduleID,
methodID,
args,
data => resolve(data),
errorData => reject(createErrorFromErrorData(errorData)),
);
});
};
} else if (type === 'sync') {
// 同步调用
fn = function(...args: Array<any>) {
return global.nativeCallSyncHook(moduleID, methodID, args);
};
}
}
let NativeModules: {[moduleName: string]: Object} = {};
if (global.nativeModuleProxy) {
NativeModules = global.nativeModuleProxy;
} else if (!global.nativeExtensions) {
// 初始化jsBridge
const bridgeConfig = global.__fbBatchedBridgeConfig;
...
// 调用原生模块
const info = genModule(config, moduleID);
if (!info) {
return;
}
if (info.module) {
NativeModules[info.name] = info.module;
}
...
}
module.exports = NativeModules;
复制代码
NativeModules
经过genModule
获取到原生模块,又经过genMethod
调用原生模块的方法。
调用原生方法分同步和异步两种方式。
以同步调用为例,它调用了global.nativeCallSyncHook
,即JSIExecutor.cpp
注册的C++的方法:
// JSIExecutor.cpp
// 注册了nativeCallSyncHook方法供js调用
runtime_->global().setProperty(
*runtime_,
"nativeCallSyncHook",
Function::createFromHostFunction(
*runtime_,
PropNameID::forAscii(*runtime_, "nativeCallSyncHook"),
1,
[this](
jsi::Runtime&,
const jsi::Value&,
const jsi::Value* args,
size_t count) { return nativeCallSyncHook(args, count); }));
Value JSIExecutor::nativeCallSyncHook(const Value* args, size_t count) {
...
// 调用委托,即ModuleRegistry,的callSerializableNativeHook函数
MethodCallResult result = delegate_->callSerializableNativeHook(
*this,
static_cast<unsigned int>(args[0].getNumber()),
static_cast<unsigned int>(args[1].getNumber()),
dynamicFromValue(*runtime_, args[2]));
if (!result.hasValue()) {
return Value::undefined();
}
return valueFromDynamic(*runtime_, result.value());
}
复制代码
JSIExecutor.cpp
中是经过委托来实现的,最终调用的仍是ModuleRegistry.cpp
// ModuleRegistry.cpp
MethodCallResult ModuleRegistry::callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params) {
...
// 原生模块被调用处
return modules_[moduleId]->callSerializableNativeHook(methodId, std::move(params));
}
复制代码
最后又到了ModuleRegistry
,也就是注册过程的终点,调用过程也就结束了。