React Native与Android原生之间的通讯

在React Native中可以通过在java层自定义@ReactMethod方式给JavaScript调用,这样在JavaScript层就可以直接调用Android中的Native方法。但是很多场景下需要我们去回调获取结果。这时候就需要实现RN与原生之间的通讯。

对于RN与Android原生之间的通讯,主要有以下几种方式帮助我们来实现:

  • Callback
  • Promise
  • RCTDeviceEventEmitter
  • startActivityForResult中获取结果

1. Callback

Callback提供了一个函数来把返回值传回给JavaScript。

Callback是react.bridge中的一个接口,它作为ReactMethod的一个传参,用来映射JavaScript的回调函数(function)。Callback接口只定义了一个方法invokeinvoke接受多个参数,这个参数必须是react.bridge中支持的参数。

一下的例子都以Toast作为例子。

定义用于实现RN的回调:

class ToastModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {    override fun getName(): String {        return "ToastExample"    }

接下来实现使用Callback方式的ReactMethod,在这里只实现了一个Callback的方式,你也可以将Callback分成两个,分别是successCallback与errorCallBack来区分成功与失败的情况:

/*** Callback方式实现通信* */@ReactMethodfun showToastWithCallback(message: String, callback: Callback) {    try {        Toast.makeText(reactApplicationContext, message, Toast.LENGTH_SHORT).show()        callback.invoke("Callback Invoke Message")    } catch (e: Exception) {        callback.invoke(e.message)    }}

在JavaScript里可以这样使用,在点击后回调会弹出Toast与Alert窗口:

<Button    onPress={() => {        NativeModules.ToastExample.showToastWithCallback("CallBack", msg => {            Alert.alert("Message", `Message: ${msg}`, null);            });        }}    title="Callback"    color="#841584"/>

React Native与Android原生之间的通讯_第1张图片

但是需要注意的是:callback 并非在对应的原生函数返回后立即被执行——注意跨语言通讯是异步的,这个执行过程会通过消息循环来进行。

2. Promises

Promise是ES6中增加的对于异步编程和回调更加友好的API,使用Promise可以更简洁,更灵活地处理回调。搭配 ES2016(ES7)标准的async/await语法则效果更佳。

在react.briage中定义的Promise接口,实现了resolvereject的方法,resolve用来处理正确处理结果的情况,reject用来处理异常的情况。

使用Promise定义ReactMethod:

/*** Promise方式实现通信* */@ReactMethodfun showToastWithPromise(message: String, promise: Promise) {    try {        Toast.makeText(reactApplicationContext, message, Toast.LENGTH_SHORT).show()        val thread = Thread.currentThread().name        promise.resolve("Promise Resolve Message and Current Thread: $thread")    } catch (e: Exception) {        promise.reject("Error", e)    }}

JavaScript 端的方法会返回一个 Promise:

<Button    onPress={() => {        NativeModules.ToastExample.showToastWithPromise("Promise")        .then(threadName =>            Alert.alert("Message", `${threadName}`, null)        )        .catch(err =>            Alert.alert(            "Error",            `get thread name error: ${err.message}`,            null            )        );    }}    title="Promise"    color="#379031"/>

React Native与Android原生之间的通讯_第2张图片

使用Promise比使用Callback更加的简洁,还能更加灵活的在多线程之间进行切换。

3. RCTDeviceEventEmitter

还有一种简单的方式是使用RCTDeviceEventEmitter

在Java中的实现如下:

/*** RCTDeviceEventEmitter(组件通讯)实现* */@ReactMethodfun showToastWithSendEvent(message: String) {    val params = Arguments.createMap().apply {        putString("key", message)    }    this.reactApplicationContext        .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)        .emit("onSendEventResult", params)}

在JS则需要增加一些操作:

import { DeviceEventEmitter } from "react-native";...export default class XXX extends Component<Props> {    componentDidMount() {        //接受到通知后的处理    this.listener = DeviceEventEmitter.addListener("onSendEventResult", msg => {      NativeModules.ToastExample.showToast(msg.key, ToastExample.LONG);    });  }  componentWillUnmount() {      //异除,不要忘记    this.listener.remove();  }  ...  <Button    onPress={() => {        //点击事件        NativeModules.ToastExample.showToastWithSendEvent("Send Event");    }}    title="Send Event"    color="#771232"  />}

React Native与Android原生之间的通讯_第3张图片

4. 从startActivityForResult中获取结果

如果你使用startActivityForResult调起了一个 activity 并想从其中获取返回结果,那么你需要监听onActivityResult事件。具体的做法是继承BaseActivityEventListener或是实现ActivityEventListener。我们推荐前一种做法,因为它相对来说不太会受到 API 变更的影响。然后你需要在模块的构造函数中注册这一监听事件。

其实是对Promise方式的一种延伸。

reactContext.addActivityEventListener(mActivityResultListener);

现在你可以通过重写下面的方法来实现对onActivityResult的监听:

@Overridepublic void onActivityResult(  final Activity activity,  final int requestCode,  final int resultCode,  final Intent intent) {  // 在这里实现你自己的逻辑}

下面我们是简单的图片选择器来实践一下。这个图片选择器会把pickImage方法暴露给 JavaScript,而这个方法在调用时就会把图片的路径返回到 JS 端。

public class ImagePickerModule extends ReactContextBaseJavaModule {  private static final int IMAGE_PICKER_REQUEST = 467081;  private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";  private static final String E_PICKER_CANCELLED = "E_PICKER_CANCELLED";  private static final String E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER";  private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND";  private Promise mPickerPromise;  private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {    @Override    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {      if (requestCode == IMAGE_PICKER_REQUEST) {        if (mPickerPromise != null) {          if (resultCode == Activity.RESULT_CANCELED) {            mPickerPromise.reject(E_PICKER_CANCELLED, "Image picker was cancelled");          } else if (resultCode == Activity.RESULT_OK) {            Uri uri = intent.getData();            if (uri == null) {              mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "No image data found");            } else {              mPickerPromise.resolve(uri.toString());            }          }          mPickerPromise = null;        }      }    }  };  public ImagePickerModule(ReactApplicationContext reactContext) {    super(reactContext);    // Add the listener for `onActivityResult`    reactContext.addActivityEventListener(mActivityEventListener);  }  @Override  public String getName() {    return "ImagePickerModule";  }  @ReactMethod  public void pickImage(final Promise promise) {    Activity currentActivity = getCurrentActivity();    if (currentActivity == null) {      promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");      return;    }    // Store the promise to resolve/reject when picker returns data    mPickerPromise = promise;    try {      final Intent galleryIntent = new Intent(Intent.ACTION_PICK);      galleryIntent.setType("image/*");      final Intent chooserIntent = Intent.createChooser(galleryIntent, "Pick an image");      currentActivity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST);    } catch (Exception e) {      mPickerPromise.reject(E_FAILED_TO_SHOW_PICKER, e);      mPickerPromise = null;    }  }}

5. 总结

方法 优点 缺点
Callback JS调用,Native返回 CallBack为异步操作,返回时机不确定
Promise JS调用,Native返回 每次使用需要JS调用一次
RCTDeviceEventEmitter 可任意时刻传递,Native主导控制 记得移除Listener并且保持通知名称一致(细节问题)

参考

  1. 原生模块
  2. react-native调用Native:回调(Android)
  3. Sending events to JavaScript from your native module in React Native

更多相关文章

  1. Android与Unity交互调用mUnityPlayer.quit()方法退出返回上一个A
  2. Unity3D调用android方法(非插件方式)
  3. Android事件处理方法总结-Handler消息处理
  4. Android原生方式获取经纬度和城市信息
  5. Unity3d Android SDK接入解析(二)Unity3d Android SDK的设计与两种
  6. android介绍以及学习方法
  7. Android开发利器之pidcat安装方式

随机推荐

  1. 注册效果与简易后台界面演示如何用html代
  2. 2022年0705表单与后台框架轻量级开发演示
  3. html表单元素以及多媒体标签
  4. 2022年0705表单与后台框架轻量级开发演示
  5. 列表的使用和图片链接以及表格的创建合并
  6. 内联框架标签与多媒体示例
  7. 列表和TABLE
  8. html简单导航与简洁课程表代码演示
  9. 跟着大牛学PHP--HTML标签和表格
  10. 实例用户注册及内联框架常用元素代码