React Native与原生模块、组件之间的关系浅析(二)
那么书接上回,今天主要是继续探究React Native与原生模块的架构方式。
原生模块
原生模块可以访问activity、运行环境、GPS、存储空间等。原生模块就是能够在JavaScript层调用的API。
因为对原生模块的全部请求都要异步执行。如果原生方法需要为JavaScript层的调用返回数据,该操作将通过promise或者回调函数来完成。React Native为这两种方式都提供了接口。
剖析原生模块
IOS和Android对原生模块的实现思路是这样的:
- 每个模块继承自原生模块父类。
- 每个模块都定义了名称,以便
JavaScript层访问。 - 每个模块都导出可调用的方法,并包含
Java的注释或Objective-C的宏。
01. Android
Android平台的原生模块继承ReactContextBaseJavaModule ,并且必须实现getName 方法,它的返回值作为JavaScript模块的名称。
任何允许JavaScript层调用的方法,都带有@ReactMethod 注释。React的内部机制会把JavaScript层的请求映射到这些方法上。每个方法的身份由它的签名、名称以及参数进行鉴别。方法所需的任何参数都会被转换成对应的属性类型。
简单的示例在上一篇已经提及,请参阅React Native与原生模块、组件之间的关系浅析
02. JavaScript
JavaScript层把原生模块作为NativeModules 对象的一个属性,并且任何带有@ReactMethod 注释或者属于RCT_EXPORT_METHOD宏的方法都能够被调用。
参数
原生模块的方法和普通方法一样可以接受参数。
针对于Android,带有@ReactMethod注释的方法支持以下参数类型。
Boolean->BoolIntegar->NumberDouble->NumberFloat->NumberString->StringCallback->functionReadableMap->ObjectReadableArray->Array
回调函数和promise
由于所有的通信过程都是异步的,原生模块不能有返回值。React Native使用回调函数和promise类作为解决方案。使用方式与在JavaScript代码中执行回调函数以及promise的resolve/reject 操作完全一样。
01 回调函数
原生回调函数接口遵循两个参数的约定:
- 第一参数表示错误对象(没有错误的情况则为null)。
- 第二参数用来提供要响应的数据。
示例:
@ReactMethod
public void add (int a, int b, Callback callback) {
int sum = a + b;
callback.invoke(null, sum);
}
02 promise
promise可以在操作成功后把值返回给JavaScript层。响应要么成功要么失败。JavaScript层的接口保留了我们所熟悉的.then 和.catch方法。
示例:
@ReactMethod
public void failIfFalse (boolean value, Promise promise) {
if (value) {
promise.resolve("Your value was true, so it resolved.");
} else {
promise.reject(new Error("Your value was false, so it rejected"));
}
}
03 返回请求成功的对象
我们已经了解到,可以通过回调函数和promise在请求成功后把标量值返回给JavaScript层。大多数情况下响应内容应该是JSON对象的形式。
Android提供了WritableMap 来返回JSON响应。该接口和包含特定类型put方法的Map很相似。
pushNullputBooleanputDoubleputIntputStringputArrayputMap
String类型的键名作为每个方法的第一参数。
第二参数是方法名相关类型的值。
示例:
@ReactMethod
public void jsonResponse(Promise promise) {
WritableMap jsonMap = new WritableNativeMap();
jsonMap.putString("stringValue", "A string");
jsonMap.putBoolean("booleanValue", true);
jsonMap.putInt("intValue", 123);
promise.resolve(jsonMap);
}
常量
原生模块也可以导出常量,供JavaScript访问。
要使原生模块的常量可用,需要实现父类的getConstants (Android)或constantsToExport (iOS)方法,这两个方法将返回键值对的映射,键名会作为JavaScript模块的属性而存在。
示例:
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("HELLO", "world");
constants.put("FOO", "bar");
}
事件
DeviceEventEmitter模块用于把事件从原生层传到JavaScript层。
针对Android而言,ReactContext暴露出getJSModule 方法用于获取其他模块,为它传入类参数后返回相应的实例。它将用来获取RCTDeviceEventEmitter模块并调用emit 方法,接着事件就会通过桥接层发送到JavaScript层,DeviceEventEmitter模块的监听器就会捕获这个事件,并通知其他任意的事件监听器。
示例:
@ReactMethod
public void sendTestEvent() {
String eventName = "customEvent";
WritableMap params = new WritableNativeMap();
params.putString("param", "Hello world");
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
在JavaScript上面的调用方式如下:
import {DeviceEventEmitter, NativeModules} from 'react-native';
const {ExampleModule} = NativeModules;
DeviceEventEmitter.addListener('customEvent', e => {
console.log('received event', e.nativeEvent.param); // 2
});
ExampleModules.sendTestEvent(); // 1
总结
通过本篇的学习,了解了原生模块和组件的工作原理。知道了react native与原生层的调用关系。原生模块就是JavaScript层可以调用的一些方法的集合。
更多推荐
所有评论(0)