RN与原生交互(二)——数据传递

我的上篇文章中简单介绍了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。具体实现方案如下:

  1. 使Module类继承RCTEventEmitter,重写supportedEvents方法,在这个方法中声明支持的事件名称。
  2. 在Module类的init方法中使用NSNotificationCenter监听iOS端要发送事件的操作。
  3. 在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();
  }

作者:不變旋律
链接:https://juejin.im/post/5b20ceb16fb9a01e4f47cd49
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

猜你喜欢

转载自blog.csdn.net/sinat_17775997/article/details/80702729
今日推荐