模块的定义
要实现通信,需要先定义ReactContexBaseJavaModule,并将ReactPackage注册到ReactInstanceManager中
ReactMethod
注意,@ReactMethod 注解的方法必须满足以下条件:
- 方法必须是公开的(public)。
- 方法不能是静态的(static)。
- 方法的返回类型必须是 void。
- 方法的参数类型必须是 React 支持的类型,例如 String、ReadableMap、Callback、Promise 等。
用法
在module中定义可以被JS调用的函数
1. 使用集成到现有Android应用
中的IndexModule文件,添加一个ReactMethod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
public class IndexModule extends ReactContextBaseJavaModule {
@NonNull @Override public String getName() { return "IndexModule"; }
@ReactMethod public void show(String message, int duration) { Toast.makeText(getReactApplicationContext(), message, duration).show(); }
private static final String DURATION_SHORT_KEY = "SHORT"; private static final String DURATION_LONG_KEY = "LONG";
@Nullable @Override public Map<String, Object> getConstants() { final Map<String, Object> constants = new HashMap<>(); constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT); constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG); return constants; } }
|
ReactMethod支持的参数类型映射关系
- Boolean -> Bool
- Integer -> Number
- Double -> Number
- Float -> Number
- String -> String
- Callback -> function
- ReadableMap -> Object
- ReadableArray -> Array
2. js调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
import {NativeModules} from 'react-native';
export default NativeModules.index;
import ToastExample from './ToastExample'; ToastExample.show('hoho', ToastExample.SHORT);
|
同步方法
如果需要同步返回结果,可以使用isBlockingSynchronousMethod
参数,该方法会阻塞JS线程,直到返回结果
1 2 3 4 5
| @ReactMethod(isBlockingSynchronousMethod = true) public String getUniqueIdSync() { return "uniqueId"; }
|
1 2 3
| import { NativeModules } from 'react-native';
let uniqueId = NativeModules.MyModule.getUniqueIdSync();
|
回调函数Callback
Callback可以在JS调用原生的方法里回调JS方法
1 2 3 4 5 6 7 8 9
| @ReactMethod public void testCallback(String param1, Callback successCallback, Callback errorCallback) { try { successCallback.invoke(param1); } catch (Exception e) { errorCallback.invoke("error:" + e.getMessage()); } }
|
1 2 3 4 5 6 7 8 9
| <Button onPress={()=>{ NativeModules.IndexModule.testCallback("测试回调", (msg)=>{ NativeModules.IndexModule.show(msg, NativeModules.IndexModule.SHORT); }, (error) => { NativeModules.IndexModule.show(error, NativeModules.IndexModule.SHORT); }); }} title={"Callback测试"}></Button>
|
Promise
使用Promise,可以在声明了async的异步函数内使用await关键字来调用
1 2 3 4 5 6 7 8 9 10
| @ReactMethod public void testPromise(String param1, Promise promise) { WritableMap map = Arguments.createMap(); map.putDouble("relativeX",1); map.putDouble("relativeY", 1); map.putDouble("width", 2); map.putDouble("height",3);
promise.resolve(map); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| <Button onPress={ async ()=> { var { relativeX, relativeY, width, height, } = await NativeModules.IndexModule.testPromise('Awesome'); NativeModules.IndexModule.show(result, NativeModules.IndexModule.SHORT);
}} title={"测试Promise"}></Button>
|
发送事件
原生模块可以在没有被调用的情况下通过RCTDeviceEventEmitter往JavaScript发送事件通知,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
@ReactMethod public void testSendEvent(String param1) { WritableMap params = Arguments.createMap(); params.putString("eventProperty", param1); setEvent("EventReminder", params); }
public void setEvent(String eventName, WritableMap params) { getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit(eventName, params); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <Button onPress={ ()=> { NativeModules.IndexModule.testSendEvent('emitttt'); }} title={"测试Event Emitter"}></Button>
function compnentDidMount() { const eventEmitter = new NativeEventEmitter(NativeModules.IndexModule); this.listener = eventEmitter.addListener('EventReminder', (reminder) => { console.log(reminder.name); console.log(reminder.location); console.log(reminder.date); NativeModules.IndexModule.show(reminder.eventProperty, NativeModules.IndexModule.SHORT); }); }
function componentWillUnmount() { this.listener && this.listener.remove(); }
compnentDidMount();
|
从startActivityResult中获取结果
运用Promise,当JS调用某个原生方法,原生打开另一个原生界面,原生界面返回的数据通过Promise再返回给JS。通过以下代码来监听回调
1
| reactContext.addActivityEventListener(mActivityResultListener);
|
具体实例
创建一个ImagePickerModule
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| package com.lqd.androidpractice.rn.modules;
import android.util.Log;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ActivityEventListener; import com.facebook.react.bridge.BaseActivityEventListener; import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod;
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";
@NonNull @Override public String getName() { return "ImagePickerModule"; }
private Promise mPickerPromise;
private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() { @Override public void onActivityResult(int requestCode, int resultCode, android.content.Intent data) { if (requestCode == IMAGE_PICKER_REQUEST) { if (mPickerPromise != null) { if (resultCode == android.app.Activity.RESULT_CANCELED) { mPickerPromise.reject(E_PICKER_CANCELLED, "Image picker was cancelled"); } else if (resultCode == android.app.Activity.RESULT_OK) { android.net.Uri uri = data.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); reactContext.addActivityEventListener(mActivityEventListener); }
@ReactMethod public void pickImage(final Promise promise) { Log.d("ImagePickerModule", "pickImage>>>>>>>>>>>>>>>>>>>>>>>>>>>"); android.app.Activity currentActivity = getCurrentActivity(); if (currentActivity == null) { promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist"); return; }
mPickerPromise = promise;
try { final android.content.Intent galleryIntent = new android.content.Intent(); galleryIntent.setType("image/*"); galleryIntent.setAction(android.content.Intent.ACTION_GET_CONTENT); final android.content.Intent chooserIntent = android.content.Intent.createChooser(galleryIntent, "Pick an image"); currentActivity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST); } catch (android.content.ActivityNotFoundException e) { mPickerPromise.reject(E_FAILED_TO_SHOW_PICKER, e); mPickerPromise = null; } }
}
|
JS测试
1 2 3 4 5 6 7 8
| <Button onPress={ async ()=>{ var result = await NativeModules.ImagePickerModule.pickImage(); console.log(result); }} title={"测试Activity Result"}></Button>
|
监听生命周期事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
reactContext.addLifecycleEventListener(new LifecycleEventListener() { @Override public void onHostResume() { }
@Override public void onHostPause() {
}
@Override public void onHostDestroy() {
} });
|