RN與原生互動(二)——資料傳遞
我的上篇文章中簡單介紹了RN與原生基本的頁面跳轉,本篇主要總結RN和原生之間的資料傳遞方式,講解RN和原生端之間如何互相傳遞資料。
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();
}