引文:有時候App需要訪問平臺API,但React Native可能還沒有相應(yīng)的模塊包裝;或者你需要復(fù)用一些Java代碼,而不是用Javascript重新實現(xiàn)一遍;又或者你需要實現(xiàn)某些高性能的、多線程的代碼,譬如圖片處理、數(shù)據(jù)庫、或者各種高級擴(kuò)展等等。
來自:https://reactnative.cn/docs/0.51/native-modules-android.html#content
看了上面的那段引文以及那篇文檔我們大致懂得在Android客戶端中如何封裝原生模塊給RN使用,本文作為補(bǔ)充說明用一些實例來更詳細(xì)的理解RN跟客戶端是如何交互。
基礎(chǔ)數(shù)據(jù)類型
自定義原生模塊給RN調(diào)用的話那么就得將Java對應(yīng)的數(shù)據(jù)類型轉(zhuǎn)換成JS可以識別的數(shù)據(jù)類型:
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
其中Callback、ReadableMap、ReadableArray這個三個除外其他的數(shù)據(jù)類型還是比較常見的,所以這里將針對每種數(shù)據(jù)類型都舉例子來明白是如何轉(zhuǎn)換和調(diào)用的。
這里假裝你看過引用的文檔并聲明相關(guān)的類...............................
常見的數(shù)據(jù)類型< Boolean、Integer、Double、Float、String >
先在AndroidStudio中定義一個類并且實現(xiàn)ReactContextBaseJavaModule類,然后聲明一個函數(shù)并用@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的代碼來調(diào)用這個原生模塊的show方法,然后我們根據(jù)打印的結(jié)果來清楚這些常見的數(shù)據(jù)類型是長怎樣的以及怎么調(diào)用。
首先RN代碼中聲明一下原生模塊:
// NativeModules后面對應(yīng)的是ToastModule是原生模塊對應(yīng)的name即可
const ToastModule = NativeModules.ToastModule;
然后RN中簡單采用Text并給他賦值一個點擊事件,當(dāng)我們點擊該Text的時候就傳遞數(shù)據(jù)給原生模塊:
// ToastModule.show是一個我們定義的原生方法
<Text onPress={() => {
ToastModule.show(false, 1, 1.0, 2.0, "來自ES6..");
}}>
基礎(chǔ)類型使用用例
</Text>
當(dāng)然我們調(diào)用ToastModule.show的時候數(shù)據(jù)類型要嚴(yán)格按照原生定義的類型來傳,不然容易報類型轉(zhuǎn)換錯誤:
// 打印結(jié)果: 很顯然都是RN傳遞給原生模塊的數(shù)據(jù)
=== boolean Message = false
=== int Message = 1
=== double Message = 1.0
=== float Message = 2.0
=== String Message = 來自ES6..
ReadableMap/ReadableArray實例
還是老樣子直接上代碼,并直接把結(jié)果打印出來 直接看注釋:
// 一個帶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對應(yīng)Js的語法是Object 而 ReadableArray對應(yīng)的語法是Array
*/
// ReadableMap我們直接傳對象{name: "Neacy"}
// ReadableArray我們傳["Jayuchou", 100]
<Text style={styles.instructions} onPress={() => {
ToastModule.showReadable({name: "Neacy"}, ["Jayuchou", 100]);
}}>
Object/Array轉(zhuǎn)ReadableMap/ReadableArraay
</Text>
// 打印結(jié)果
=== ReadableMap = Neacy
=== ReadableArray[0] = Jayuchou
=== ReadableArray[1] = 100
Callback實例
常見的數(shù)據(jù)類型都是RN傳遞給原生使用,很顯然平時開發(fā)不會這么簡單往往我們需要客戶端處理完結(jié)果后回調(diào)給RN,這里聲明一個函數(shù)直接將傳入的參數(shù)相加并將結(jié)果通過callback回調(diào)回去。
@ReactMethod
public void add(int a, int b, Callback callback) {
callback.invoke("CallBack的結(jié)果是 = " + (a + b));
}
在RN代碼中直接調(diào)用ToastModule.add方法,注意最后是一個參數(shù)是一個函數(shù),Callback往往用于做同步操作的時候比較多一點。
// 這里將通過原生計算后的結(jié)果通過setState來刷新界面并顯示出來: 效果看文章附圖
<Text style={styles.welcome} onPress={() => {
ToastModule.add(1, 2, (result) => {
this.setState({name: result});
})
}}>
CallBack使用用例
</Text>
Promise實例
很明顯有了Callback已經(jīng)可以回調(diào)數(shù)據(jù)了,為什么還要Promise呢? 為了異步,用Promise就是我們可以通過原生異步處理一個耗時的然后再將結(jié)果傳給RN端:
@ReactMethod
public void doRequest(final Promise promise) {
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
e.onNext("Promise的結(jié)果 = Neacy");
e.onComplete();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
WritableMap wMap = new WritableNativeMap();
wMap.putString("name", s);
promise.resolve(wMap);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
promise.reject(throwable);
}
});
}
有沒有覺得采用RxJava跟Promise真的好配。
// 定義一個promise異步請求,采用ES6的async await語法糖
async getInfo() {
this.setState(await ToastModule.doRequest());
}
// 點擊按鈕觸發(fā)請求,同樣請求的結(jié)果放到state中 結(jié)果查看附圖。
<Text style={styles.welcome} onPress={() => {
this.getInfo();
}}>
Promise使用用例
</Text>
DeviceEventEmitter實例
常見的數(shù)據(jù)交互除了RN主動和客戶端交互外,客戶端還可以通過DeviceEventEmitter將數(shù)據(jù)直接發(fā)給RN端,當(dāng)然RN端需要接收。
WritableMap map = Arguments.createMap();
map.putString("name", "=== 這是DeviceEventEmitter結(jié)果 ===");
// 傳一個WriteableMap給RN
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("neacy", writableMap);
// 直接傳一個String給RN
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("neacy", "=== DeviceEventEmitter的是字符串 ===");
然后RN設(shè)置監(jiān)聽,一般會在componentWillMount方法組件加載完成后設(shè)置監(jiān)聽:
componentWillMount() {
DeviceEventEmitter.addListener('neacy', (result) => {
this.setState({name: result});
})
}
附圖
上面是一個個Text可以點擊相對應(yīng)的來查看交互結(jié)果:
這就是常見的RN跟Android原生交互的一些方案:
- 直接傳數(shù)據(jù)給Android端,然后Android端可以異步或者同步處理并通過Promise或者Callback回調(diào)數(shù)據(jù)給RN端顯示。
- Android端通過DeviceEventEmitter主動和RN端進(jìn)行交互。