引文:有时候App需要访问平台API,但React Native可能还没有相应的模块包装;或者你需要复用一些Java代码,而不是用Javascript重新实现一遍;又或者你需要实现某些高性能的、多线程的代码,譬如图片处理、数据库、或者各种高级扩展等等。
来自:https://reactnative.cn/docs/0.51/native-modules-android.html#content
看了上面的那段引文以及那篇文档我们大致懂得在Android客户端中如何封装原生模块给RN使用,本文作为补充说明用一些实例来更详细的理解RN跟客户端是如何交互。
基础数据类型
自定义原生模块给RN调用的话那么就得将Java对应的数据类型转换成JS可以识别的数据类型:
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
其中Callback、ReadableMap、ReadableArray这个三个除外其他的数据类型还是比较常见的,所以这里将针对每种数据类型都举例子来明白是如何转换和调用的。
这里假装你看过引用的文档并声明相关的类...............................
常见的数据类型< Boolean、Integer、Double、Float、String >
先在AndroidStudio中定义一个类并且实现ReactContextBaseJavaModule类,然后声明一个函数并用@ReactMethod声明注释:
@ReactMethod
public void show(boolean _boolean, int _int, double _double, float _float, String _string) {
Log.w("Jayuchou", "=== boolean Message = " + _boolean);
Log.w("Jayuchou", "=== int Message = " + _int);
Log.w("Jayuchou", "=== double Message = " + _double);
Log.w("Jayuchou", "=== float Message = " + _float);
Log.w("Jayuchou", "=== String Message = " + _string);
}
然后这时候开始写一个RN的代码来调用这个原生模块的show方法,然后我们根据打印的结果来清楚这些常见的数据类型是长怎样的以及怎么调用。
首先RN代码中声明一下原生模块:
// NativeModules后面对应的是ToastModule是原生模块对应的name即可
const ToastModule = NativeModules.ToastModule;
然后RN中简单采用Text并给他赋值一个点击事件,当我们点击该Text的时候就传递数据给原生模块:
// ToastModule.show是一个我们定义的原生方法
{
ToastModule.show(false, 1, 1.0, 2.0, "来自ES6..");
}}>
基础类型使用用例
当然我们调用ToastModule.show的时候数据类型要严格按照原生定义的类型来传,不然容易报类型转换错误:
// 打印结果: 很显然都是RN传递给原生模块的数据
=== boolean Message = false
=== int Message = 1
=== double Message = 1.0
=== float Message = 2.0
=== String Message = 来自ES6..
ReadableMap/ReadableArray实例
还是老样子直接上代码,并直接把结果打印出来 直接看注释:
// 一个带ReactMethod的方法并传入ReadableMap ReadableArray
@ReactMethod
public void showReadable(ReadableMap object, ReadableArray array) {
Log.w("Jayuchou", "=== ReadableMap = " + object.getString("name"));
Log.w("Jayuchou", "=== ReadableArray[0] = " + array.getString(0));
Log.w("Jayuchou", "=== ReadableArray[1] = " + array.getInt(1));
}
/**
首先要明白:
ReadableMap对应Js的语法是Object 而 ReadableArray对应的语法是Array
*/
// ReadableMap我们直接传对象{name: "Neacy"}
// ReadableArray我们传["Jayuchou", 100]
{
ToastModule.showReadable({name: "Neacy"}, ["Jayuchou", 100]);
}}>
Object/Array转ReadableMap/ReadableArraay
// 打印结果
=== ReadableMap = Neacy
=== ReadableArray[0] = Jayuchou
=== ReadableArray[1] = 100
Callback实例
常见的数据类型都是RN传递给原生使用,很显然平时开发不会这么简单往往我们需要客户端处理完结果后回调给RN,这里声明一个函数直接将传入的参数相加并将结果通过callback回调回去。
@ReactMethod
public void add(int a, int b, Callback callback) {
callback.invoke("CallBack的结果是 = " + (a + b));
}
在RN代码中直接调用ToastModule.add方法,注意最后是一个参数是一个函数,Callback往往用于做同步操作的时候比较多一点。
// 这里将通过原生计算后的结果通过setState来刷新界面并显示出来: 效果看文章附图
{
ToastModule.add(1, 2, (result) => {
this.setState({name: result});
})
}}>
CallBack使用用例
Promise实例
很明显有了Callback已经可以回调数据了,为什么还要Promise呢? 为了异步,用Promise就是我们可以通过原生异步处理一个耗时的然后再将结果传给RN端:
@ReactMethod
public void doRequest(final Promise promise) {
Observable.create(new ObservableOnSubscribe() {
@Override
public void subscribe(ObservableEmitter e) throws Exception {
e.onNext("Promise的结果 = Neacy");
e.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer() {
@Override
public void accept(String s) throws Exception {
WritableMap wMap = new WritableNativeMap();
wMap.putString("name", s);
promise.resolve(wMap);
}
}, new Consumer() {
@Override
public void accept(Throwable throwable) throws Exception {
promise.reject(throwable);
}
});
}
有没有觉得采用RxJava跟Promise真的好配。
// 定义一个promise异步请求,采用ES6的async await语法糖
async getInfo() {
this.setState(await ToastModule.doRequest());
}
// 点击按钮触发请求,同样请求的结果放到state中 结果查看附图。
{
this.getInfo();
}}>
Promise使用用例
DeviceEventEmitter实例
常见的数据交互除了RN主动和客户端交互外,客户端还可以通过DeviceEventEmitter将数据直接发给RN端,当然RN端需要接收。
WritableMap map = Arguments.createMap();
map.putString("name", "=== 这是DeviceEventEmitter结果 ===");
// 传一个WriteableMap给RN
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("neacy", writableMap);
// 直接传一个String给RN
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("neacy", "=== DeviceEventEmitter的是字符串 ===");
然后RN设置监听,一般会在componentWillMount方法组件加载完成后设置监听:
componentWillMount() {
DeviceEventEmitter.addListener('neacy', (result) => {
this.setState({name: result});
})
}
附图
上面是一个个Text可以点击相对应的来查看交互结果:
这就是常见的RN跟Android原生交互的一些方案:
- 直接传数据给Android端,然后Android端可以异步或者同步处理并通过Promise或者Callback回调数据给RN端显示。
- Android端通过DeviceEventEmitter主动和RN端进行交互。