在做React Native開發的時候避免不了的需要原生子產品和JS之間進行資料傳遞,這篇文章将向大家分享原生子產品向JS傳遞資料的幾種方式。
方式一:通過Callbacks的方式
說起Callbacks大家都不陌生,它是最常用的設計模式之一。無論是Java,Object-c,C#,還是JavaScript等都會看到Callbacks的身影。
原生子產品支援Callbacks類型的參數,該Callbacks對應JS中的function。
在原生子產品中:
public class RNTestModule extends ReactContextBaseJavaModule{
public RNTestModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "RNTest";
}
@ReactMethod
public void measureLayout(
int tag,
int ancestorTag,
Callback errorCallback,
Callback successCallback) {
try {
measureLayout(tag, ancestorTag, mMeasureBuffer);
map.putDouble("relativeX",1);
map.putDouble("relativeY", 1);
map.putDouble("width", 2);
map.putDouble("height",3);
successCallback.invoke(relativeX, relativeY, width, height);
} catch (IllegalViewOperationException e) {
errorCallback.invoke(e.getMessage());
}
}
在上述代碼中,
measureLayout
方法的參數中後兩個就是Callbacks,當原生子產品處理成功時通過successCallback回調來告知JS處理成功的結果,當原生子產品發生異常時,則通過errorCallback回調來JS處理異常。
在JS子產品中:
RNTest.measureLayout(
100,
100,
(msg) => {
console.log(msg);
},
(x, y, width, height) => {
console.log(x + ':' + y + ':' + width + ':' + height);
}
);
上述代碼,是在JS子產品中調用原生子產品的方法
measureLayout
,同時向它傳遞了四個參數,後兩個是function類型的參數用于接收原生子產品的處理結果。
通過上述的方式,JS調用原生子產品的
measureLayout
方法,原生子產品則通過
errorCallback
與
successCallback
Callbacks來将處理結果傳遞到JS。
這種“You call me,I will callback”,的方式小夥伴們都看懂了吧。
方式二:通過Promises的方式
Promises是ES6的一個新的特性,在React Native中你會看到Promises的大量使用。
原生子產品也是支援Promises的,這對喜歡使用Promises的小夥伴則是一個很好的消息。
在原生子產品中:
public class RNTestModule extends ReactContextBaseJavaModule{
public RNTestModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "RNTest";
}
@ReactMethod
public void measureLayout(
int tag,
int ancestorTag,
Promise promise) {
try {
WritableMap map = Arguments.createMap();
map.putDouble("relativeX",1);
map.putDouble("relativeY", 1);
map.putDouble("width", 2);
map.putDouble("height",3);
promise.resolve(map);
} catch (IllegalViewOperationException e) {
promise.reject(e);
}
}
}
上述代碼中,
measureLayout
方法接收的最後一個為Promise,當相應的處理結果出來之後原生子產品通過調用Promise的相應方法來向JS子產品傳遞處理成功,或處理失敗的資料。
提示:在原生子產品中Promise類型的參數要放在最後一位,這樣JS調用的時候才能傳回一個Promise。
在JS子產品中:
async test() {
try {
var {
relativeX,
relativeY,
width,
height,
} = await RNTest.measureLayout(100, 100);
console.log(relativeX + ':' + relativeY + ':' + width + ':' + height);
} catch (e) {
console.error(e);
}
}
在上述代碼中,通過ES7的新特性async/await來修飾了
test
方法,來以同步方式調用原生子產品的
measureLayout
方法,如果原生子產品處理成功, 那麼JS中relativeX,relativeY,width,height會獲得相應的值,如果原生子產品處理失敗,則會抛出異常。
如果,不希望以同步的形式調用,可以這樣寫:
test2(){
RNTest.measureLayout(100,100).then(e=>{
console.log(e.relativeX + ':' + e.relativeY + ':' + e.width + ':' + e.height);
this.setState({
relativeX:e.relativeX,
relativeY:e.relativeY,
width:e.width,
height:e.height,
})
}).catch(error=>{
console.log(error);
});
}
以上就是通過Promises的方式向JS傳遞資料的方式,小夥伴們看懂了嗎。
上述兩種方式,通過Callbacks的方式與通過Promises的方式,都可以向JS子產品傳遞資料,但都是隻能傳遞一次。 如果,你需要多次向JS子產品傳遞資料(如:按鍵事件)上述方式還是不夠好,下面就像大家分享可以多次傳遞資料的方式。
方式三:通過發送事件的方式
原生子產品支援另外一種向JS子產品傳遞資料的方式,通過發送事件的方式。
原生子產品,可以向JS傳遞事件而不要而不需要直接的調用,就像Android中的廣播,iOS中的通知中心。
下面就向大家示範通過
RCTDeviceEventEmitter
,來向JS傳遞事件。
在原生子產品中:
@Override
public void onHandleResult(String barcodeData) {
WritableMap params = Arguments.createMap();
params.putString("result", barcodeData);
sendEvent(getReactApplicationContext(), "onScanningResult", params);
}
private void sendEvent(ReactContext reactContext,String eventName, @Nullable WritableMap params) {
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
上述代碼向JS子產品發送了一個名為“onScanningResult”的事件,并攜帶了“params”作為參數。
在JS子產品中:
下面是在JS代碼中進行監聽原生子產品發出的名為“onScanningResult”的事件。
componentDidMount() {
//注冊掃描監聽
DeviceEventEmitter.addListener('onScanningResult',this.onScanningResult);
}
onScanningResult = (e)=> {
this.setState({
scanningResult: e.result,
});
// DeviceEventEmitter.removeListener('onScanningResult',this.onScanningResult);//移除掃描監聽
}
在JS中通過
DeviceEventEmitter
注冊監聽了名為“onScanningResult”的事件,當原生子產品發出名為“onScanningResult”的事件後,綁定在該事件上的
onScanningResult = (e)
會被回調。 然後通過
e.result
就可獲得事件所攜帶的資料。
心得:如果在JS中有多處注冊了事件,那麼當原生子產品發出事件後,這幾個地方會同時收到該事件。不過大家也可以通過
onScanningResult
來移除對名為“onScanningResult”事件的監聽。
DeviceEventEmitter.removeListener('onScanningResult',this.onScanningResult)
另外,JS子產品也支援通過
Subscribable
mixin,也注冊監聽事件,因為ES6已經不再推薦使用mixin,是以在這裡也就不向大家介紹了。
三種方式的優缺點
方式 | 缺點 | 優點 |
---|---|---|
通過Callbacks的方式 | 隻能傳遞一次 | 傳遞可控,JS子產品調用一次,原生子產品傳遞一次 |
通過Promises的方式 | 隻能傳遞一次 | 傳遞可控,JS子產品調用一次,原生子產品傳遞一次 |
通過發送事件的方式 | 原生子產品主動傳遞,JS子產品被動接收 | 可多次傳遞 |