此文基於react natve的 September 2018 - revision 5 版本
在我的上一篇文章《帶你徹底看懂React Native和Android原生控制元件之間的對映關係》中,我已經完整地剖析了從RN元件到原生控制元件之間的對映關係,文中簡單地提到了一些通訊原理,本文我就來詳細地講解一下RN的通訊原理。
PS:網上講解RN通訊原理的相關文章很多,但良莠不齊,有的程式碼氾濫,邏輯混亂,有的過於簡單,結構不清,有的程式碼嚴重過時,已不是當前RN主流版本的原始碼。本文的目的就是想讓讀者對最新的RN通訊原理有一個清晰的認識。Let's get started!
原理簡述
RN通訊原理簡單地講就是,一方將其部分方法註冊成一個對映表,另一方再在這個對映表中查詢並呼叫相應的方法,而jsBridge擔當兩者間橋接的角色。
原始碼解析
按原理簡述中的順序,我將本節分成兩部分,一是從native(java)出發的註冊過程,二是從js出發的呼叫過程,中間還穿插了部分jsBridge中的C++內容。
註冊過程
先看官方教程中的例子:
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。
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()
複製程式碼
以上的程式碼都是官方教程中的程式碼。
由上面的程式碼可見NativeModule
被新增到了ReactPackage
中並被傳入了ReactInstanceManager
中。寫過RN的人對ReactInstanceManager
肯定不會陌生,寫RN所在的Activity時必然會例項化ReactInstanceManager
,RN在Android端幾乎所有的通訊邏輯都在它內部完成。
接下來開始原始碼的分析:
// 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
,也就是註冊過程的終點,呼叫過程也就結束了。