内容简介:此文基于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的通信原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web 2.0 Architectures
Duane Nickull、Dion Hinchcliffe、James Governor / O'Reilly / 2009 / USD 34.99
The "Web 2.0" phenomena has become more pervasive than ever before. It is impacting the very fabric of our society and presents opportunities for those with knowledge. The individuals who understand t......一起来看看 《Web 2.0 Architectures》 这本书的介绍吧!