内容简介:此文基于react natve的在我的上一篇文章PS:网上讲解RN通信原理的相关文章很多,但良莠不齐,有的代码泛滥,逻辑混乱,有的过于简单,结构不清,有的代码严重过时,已不是当前RN主流版本的源码。本文的目的就是想让读者对最新的RN通信原理有一个清晰的认识。Let's get started!
此文基于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
,也就是注册过程的终点,调用过程也就结束了。
至此,注册过程和调用过程无缝衔接,一个完整的通信过程已经跃然纸上。
以上所述就是小编给大家介绍的《重新认识React Native和Android的通信原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。