安卓系統為我們提供了webview來載入網頁,為了讓webview載入的網頁可以與App互動,系統提供了一套機制幫助我們更方便的實現通訊。同樣為了實現React Native與原生App之間的通訊,Facebook也實現了自己的一套互動機制.
通過平時與IOS和安卓的同學同事除錯,大致歸納了有以下四種:
RCTDeviceEventEmitter 事件方式
Callback 回撥方式
Promise 信任方式
直傳常量資料
先比較下優缺點:eventMitter的形式可任意時刻傳遞,由Native主導控制,callback形式由JS呼叫Native返回,但是是非同步的時機不確定,所以有Promise形式,但是要不斷的JS呼叫如輪詢。直傳常量跨域傳值,只能從原生端向RN端傳遞。RN端可通過 NativeModules.[module名].[引數名] 的方式獲取
瞭解了三者的通訊方式,怎麼能少了程式碼的描述!我們來看看程式碼如何實現。大致的實現步驟如下:
以下是安卓程式碼的編寫 eventMitter
1. 定義Module類,繼承ReactContextBaseJavaModule, 在Module類中,我們定義互動的方法,例如RN呼叫Native的方法,Native呼叫RN的方法等。
@Override
public String getName() {
return MODULE_NAME;
}
/**
* RN呼叫Native的方法在module中定義一個方法,並用@ReactMethod 註解標註:表明該方法會被RN呼叫。即被RN呼叫的原生方法必須使用@ReactMethod註解標註。
*/
@ReactMethod
public void rnCallNative(String a) {
// XXX.
mContext.startActivity(XX);
}
/**
* Native呼叫RN 上面程式碼定義了原生方法,通過在Android層呼叫RN層。使用ReactContext的getJSModule方法,emit來傳送訊息。同樣,emit的第一個引數要與RN層中addListener方法的第一個引數相同。
* @param msg
*/
public void nativeCallRn(String msg) { mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit( EVENT_NAME,msg);
}
2. 定義Package類,繼承ReactPackage實現Package的createNativeModules方法,將Module例項新增到集合。
/** 通訊Package類 */
public class CommPackage implements ReactPackage {
public CommModule mModule;
/**
* 建立Native Module 在createNativeModules方法中,初始化集合,並將module例項新增進集合,返回集合例項。
* @param reactContext
*/
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
mModule = new CommModule(reactContext);
modules.add(mModule);
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
3. 定義Application,繼承ReactApplication實現getPackages方法,將Package例項新增到getPackages下的集合。
private static final CommPackage mCommPackage = new CommPackage();
**
* 獲取 reactPackage getPackages方法中,將Package例項新增到Arrays中即可完成註冊
*/
public static CommPackage getReactPackage() {
return mCommPackage;
}
以下是RN程式碼的編寫,通過 RCTDeviceEventEmitter 模式進行通訊互動。互動都是以主動方式為主
1. 呼叫原生程式碼
/**
* 呼叫原生程式碼 在React Native層,通過NativeModules呼叫commModule
*/
nativeCall() {
NativeModules.commModule.rnCallNative(xxx);
}
2. 接收原生呼叫
/**
* 接收原生呼叫通過DeviceEventEmitter註冊監聽,類似於Android中的監聽事件。第一個引數標識名稱,要與Module中emit的Event Name相同。第二個引數即為處理回掉IOS應該為NativeEventEmitter
*/
componentDidMount() {
DeviceEventEmitter.addListener('nativeCallRn',(msg)=>{ });
}
Callback形式 RN層呼叫Native層,Native層處理完成後,回撥RN層
**
* Callback 方式
* rn呼叫Native,並獲取返回值
*/
@ReactMethod
public void rnCallNativeCb(String msg, Callback callback) {
callback.invoke(xxx); // 2.回撥RN,即將處理結果返回給RN
}
// RN
callbackComm(msg) {
NativeModules.commModule.rnCallNativeCb(msg,(result) => {})
}
Promise形式 同樣是RN層呼叫Native層,Native層處理完成後,回撥RN層
@ReactMethod
public void rnCallNativePromise(String msg, Promise promise) {
promise.resolve(xxx);
}
// RN
promiseComm(msg) {
NativeModules.commModule.rnCallNativePromise(msg).then((result) =>{}).catch((error) => {console.log(error)});
}
直傳常量資料(原生向RN)
@Nullable
@Override
@return a map of constants this module exports to JS. Supports JSON types.
從原始碼註釋中可以看出,該方法是返回一個Map型別的常量,匯出到JS端(即RN)。支援JSON 型別。所以,我們只需要重寫方法,宣告Map集合,向其中新增常量後,返回即可
public Map<String, Object> getConstants() {
return super.getConstants();
}
// Ours Android
@Nullable
@Override
public Map<String, Object> getConstants() {
Map<String,Object> params = new HashMap<>();
params.put("Constantxx","xxx");
return params;
}
// Ours RN
componentWillMount() {
let result = NativeModules.MyModule.Constantxx
}