我的上篇文章中简单介绍了RN与原生基本的页面跳转,本篇主要总结RN和原生之间的数据传递方式,讲解RN和原生端之间如何互相传递数据。
Demo地址:DataTransfer
RN向原生传递数据
在上一篇文章中已经说明了怎样分别在iOS和Android端创建module类,怎样使用。这里不再赘述。RN向原生传递数据需要我们在module类中定义相关方法,方法参数即为RN传递过来的数据,原生端根据参数做相应处理。
传递字符串
iOS代码如下:
/// RN向原生传递字符串
RCT_EXPORT_METHOD(getStringFromReactNative:(NSString *)s) {
NSString *msg = [NSString stringWithFormat:@"RN传递过来的字符串:%@", s];
[self showAlert:msg];
}
复制代码
Android如下:
/**
* RN向原生传递字符串
* @param s
*/
@ReactMethod
public void getStringFromReactNative(String s) {
Toast.makeText(mContext, s, Toast.LENGTH_SHORT).show();
}
复制代码
传递整数或其它数字类型与此类似。
传递字典
这个很关键,iOS端接收数据类型还是NSDictionary类型,但Android端接收参数可不是Map、HashMap之类的,而是ReadableMap,这里要划重点。
/**
* RN向原生传递字典。这里原生端接收RN传过来的字典类型是ReadableMap
* @param map
*/
@ReactMethod
public void getDictionaryFromRN(ReadableMap map) {
System.out.print(map);
Toast.makeText(mContext, "已收到字典数据", Toast.LENGTH_SHORT).show();
}
复制代码
iOS端核心代码:
/// RN向原生传递字典
RCT_EXPORT_METHOD(getDictionaryFromRN:(NSDictionary *)dict) {
NSLog(@"RN传递过来的字典:%@", dict);
NSString *name = [dict objectForKey:@"title"];
[self showAlert:name];
}
复制代码
传递数组
iOS端接收参数类型可定义为NSArray,但Android端接收参数需要定义为ReadableArray,不能是java中的集合类型,如List、ArrayList是解析不到数据的。
Android代码:
@ReactMethod
public void getArrayFromRN(ReadableArray array) {
System.out.print(array);
Toast.makeText(mContext, "已收到数组数据", Toast.LENGTH_SHORT).show();
}
复制代码
原生向RN回调数据
以Android端为例,RN在调用以下方法时可以通过回调获取到原生端返回的数据。
/**
* 原生通过回调的形式向RN端传递string
* @param callback
*/
@ReactMethod
public void passStringBackToRN(Callback callback) {
callback.invoke("This is a string from Native");
}
/**
* 原生通过回调的形式向RN端传递字典。这里传出去的字典类型必须是WritableMap,java中的Map、HashMap是不能传递到RN的
* @param callback
*/
@ReactMethod
public void passDictionaryBackToRN(Callback callback) {
WritableMap map = Arguments.createMap();
map.putString("name", "小明");
map.putInt("age", 20);
map.putString("gender", "male");
map.putBoolean("isGraduated", true);
callback.invoke(map);
}
/**
* 原生通过回调的形式向RN端传递数组。这里传出去的字典类型必须是WritableArray
* @param callback
*/
@ReactMethod
public void passArrayBackToRN(Callback callback) {
WritableArray array = Arguments.createArray();
array.pushString("React Native");
array.pushString("Android");
array.pushString("iOS");
callback.invoke(array);
}
@ReactMethod
public void passPromiseBackToRN(String msg, Promise promise) {
if (!msg.equals("")) {
promise.resolve(true);
} else {
promise.reject("warning", "msg cannot be empty!");
}
}
复制代码
iOS端在回调数据时一般使用RCTResponseSenderBlock,任何数据类型都以block形式返回,示例如下:
/// 回传数组到RN端
RCT_EXPORT_METHOD(passArrayBackToRN:(RCTResponseSenderBlock)block) {
if (block) {
NSArray *items = @[@"React Native", @"Android", @"iOS"];
block(@[items]);
}
}
复制代码
iOS端以promise形式返回数据与Android不同,Android端定义了一个Promise类,iOS端还是通过block形式给出回调,使用RCTPromiseResolveBlock和RCTPromiseRejectBlock
/// 以promise形式回传数据到RN端
RCT_EXPORT_METHOD(passPromiseBackToRN:(NSString *)msg resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
if (![msg isEqualToString:@""]) {
resolve(@(YES));
} else {
reject(@"warning", @"msg cannot be empty!", nil);
}
}
复制代码
发送事件
Android端核心代码:
public void sendEvent(String eventName) {
String dataToRN = "这是发给RN的字符串";
mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, dataToRN);
}
复制代码
sendEvent方法定义在module类中,在需要发送事件的地方调用sendEvent方法就可以将事件通知发给RN端。
RN端监听Android端发来的通知如下,这里假设事件名称为“CustomEventName”
componentWillMount(){
DeviceEventEmitter.addListener('CustomEventName', (e)=> {
console.log("接收到通知") ;
});
}
复制代码
iOS端与Android不同,不使用DeviceEventEmitter
做监听,而是用NativeEventEmitter
。具体实现方案如下:
- 使Module类继承
RCTEventEmitter
,重写supportedEvents
方法,在这个方法中声明支持的事件名称。 - 在Module类的init方法中使用NSNotificationCenter监听iOS端要发送事件的操作。
- 在NSNotification对应的通知方法中将事件发送给RN。
核心代码:
+ (id)allocWithZone:(struct _NSZone *)zone {
static DataTransferModule *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [super allocWithZone:zone];
});
return sharedInstance;
}
- (instancetype)init {
self = [super init];
if (self) {
NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter removeObserver:self];
[defaultCenter addObserver:self
selector:@selector(sendCustomEvent:)
name:@"sendCustomEventNotification"
object:nil];
}
return self;
}
/// 接收通知的方法,接收到通知后发送事件到RN端。RN端接收到事件后可以进行相应的逻辑处理或界面跳转
- (void)sendCustomEvent:(NSNotification *)notification {
[self sendEventWithName:@"CustomEventName" body:@"这是发给RN的字符串"];
}
/// 重写方法,定义支持的事件集合
- (NSArray<NSString *> *)supportedEvents {
return @["CustomEventName"];
}
复制代码
比如在iOS端我有个页面A,点击页面中的一个button需要关掉当前页面并且发送数据给RN端,使RN端接收到数据后做跳转操作。那么在A中button的点击方法如下:
- (void)buttonClicked:(id)sender {
[[NSNotificationCenter defaultCenter] postNotificationName:@"sendCustomEventNotification" object:nil];
[self dismissViewControllerAnimated:YES completion:nil];
}
复制代码
Module类中接收到通知后就将这个要做的操作发送给RN了。RN端监听方法如下:
const DataTransferModule = NativeModules.DataTransferModule;
componentDidMount() {
let eventEmitter = new NativeEventEmitter(DataTransferModule);
this.listener = eventEmitter.addListener("CustomEventName", (result) => {
this.showAlert("获取到事件通知" + result);
})
}
componentWillUnmount() {
this.listener && this.listener.remove();
}
复制代码