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
接口只定义了一个方法invoke
,invoke
接受多个参数,这个参数必须是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"/>
但是需要注意的是:callback 并非在对应的原生函数返回后立即被执行——注意跨语言通讯是异步的,这个执行过程会通过消息循环来进行。
2. Promises
Promise是ES6中增加的对于异步编程和回调更加友好的API,使用Promise可以更简洁,更灵活地处理回调。搭配 ES2016(ES7)标准的async/await语法则效果更佳。
在react.briage中定义的Promise
接口,实现了resolve
和reject
的方法,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"/>
使用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" />}
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并且保持通知名称一致(细节问题) |
参考
- 原生模块
- react-native调用Native:回调(Android)
- Sending events to JavaScript from your native module in React Native
更多相关文章
- Android与Unity交互调用mUnityPlayer.quit()方法退出返回上一个A
- Unity3D调用android方法(非插件方式)
- Android事件处理方法总结-Handler消息处理
- Android原生方式获取经纬度和城市信息
- Unity3d Android SDK接入解析(二)Unity3d Android SDK的设计与两种
- android介绍以及学习方法
- Android开发利器之pidcat安装方式