我的上篇文章中简单介绍了RN与原生基本的页面跳转,本篇主要总结RN和原生之间的数据传递方式,讲解RN和原生端之间如何互相传递数据。
Demo地址:
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*)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(); }复制代码