React Native与Android原生之间的通讯

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方式实现通信
* */
@ReactMethod
fun 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"
/>

在这里插入图片描述

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

2. Promises

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

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

使用Promise定义ReactMethod:

/**
* Promise方式实现通信
* */
@ReactMethod
fun 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"
/>

在这里插入图片描述

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

3. RCTDeviceEventEmitter

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

在Java中的实现如下:

/**
* RCTDeviceEventEmitter(组件通讯)实现
* */
@ReactMethod
fun 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"
  />
}

在这里插入图片描述

4. 从startActivityForResult中获取结果

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

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

reactContext.addActivityEventListener(mActivityResultListener);

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

@Override
public 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. 总结

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

参考

  1. 原生模块
  2. react-native调用Native:回调(Android)
  3. Sending events to JavaScript from your native module in React Native
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值