React Native與Android通訊互動
剛建立的React Native 微信公眾號,歡迎微信掃描關注訂閱號,每天定期會分享react
native 技術文章,移動技術乾貨,精彩文章技術推送。同時可以掃描我的微信加入react-native技術交流微信群。歡迎各位大牛,React
Native技術愛好者加入交流!
在前兩篇的內容中,和大家分享了Android原生整合RN,以及RN的增量熱更新。關於詳細的內容,點選如下具體瞭解:
本篇內容同樣和React Native 與 原生App有關,可以說更加深入了兩者之間的感情,為培養下一代做出準備:React Native與原生App的通訊互動。
Android系統為我們提供了webview來載入網頁,為了讓webview載入的網頁可以與App互動,系統提供了一套機制幫助我們更方便的實現通訊。同樣為了實現React Native與原生App之間的通訊,FB也實現了自己的一套互動機制。
(1)RCTDeviceEventEmitter 事件方式
(2)Callback 回撥方式
(3)Promise
(4)直傳常量資料(原生向RN)
三種方式各具不同優缺點
1. RCTDeviceEventEmitter
優點:可任意時刻傳遞,Native主導控制。
2. Callback
優點:JS呼叫,Native返回。
缺點:CallBack為非同步操作,返回時機不確定
3. Promise
優點:JS呼叫,Native返回。
缺點:每次使用需要JS呼叫一次
瞭解了三者的通訊方式,怎麼能少了程式碼的描述!我們來看看程式碼如何實現。大致的實現步驟如下:
(1)定義Module類,繼承ReactContextBaseJavaModule
在Module類中,我們定義互動的方法,例如RN呼叫Native的方法,Native呼叫RN的方法等。
(2)定義Package類,繼承ReactPackage
實現Package的createNativeModules方法,將Module例項新增到集合。
(3)定義Application,繼承ReactApplication
實現getPackages方法,將Package例項新增到getPackages下的集合。
4. 直傳常量資料(原生向RN)
跨域傳值,只能從原生端向RN端傳遞。RN端可通過 NativeModules.[module名].[引數名]
1.Module類中的核心程式碼
/**
* 在rn程式碼裡面是需要這個名字來呼叫該類的方法
* @return
*/
@Override
public String getName() {
return MODULE_NAME;
}
名稱可以自定義,對接時協商好即可。
/**
* RN呼叫Native的方法
* @param phone
*/
@ReactMethod
public void rnCallNative(String phone) {
// 跳轉到打電話介面
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + phone));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 跳轉需要新增flag, 否則報錯
mContext.startActivity(intent);
}
在module中定義一個方法,並用@ReactMethod 註解標註:表明該方法會被RN呼叫。即被RN呼叫的原生方法必須使用@ReactMethod註解標註。
注意:RN層呼叫Native層進行介面跳轉時,需要設定FLAG_ACTIVITY_NEW_TASK標誌,否則會出現如下錯誤:
/**
* Native呼叫RN
* @param msg
*/
public void nativeCallRn(String msg) {
mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(EVENT_NAME,msg);
}
上面程式碼定義了原生方法,通過在Android層呼叫RN層。使用ReactContext的getJSModule方法,emit來發送訊息。同樣,emit的第一個引數要與RN層中addListener方法的第一個引數相同。
2.自定義Package的核心程式碼
/**
* 通訊Package類
* Created by Song on 2017/2/17.
*/
public class CommPackage implements ReactPackage {
public CommModule mModule;
/**
* 建立Native Module
* @param reactContext
* @return
*/
@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();
}
}
在createNativeModules方法中,初始化集合,並將module例項新增進集合,返回集合例項。
3.Application核心程式碼
private static final CommPackage mCommPackage = new CommPackage();
/**
* 獲取 reactPackage
* @return
*/
public static CommPackage getReactPackage() {
return mCommPackage;
}
在getPackages方法中,將Package例項新增到Arrays中即可完成註冊。以上就是Android層核心程式碼配置,繼續來看React Native層核心程式碼:
1.呼叫原生程式碼
/**
* 呼叫原生程式碼
*/
skipNativeCall() {
let phone = '18637070949';
NativeModules.commModule.rnCallNative(phone);
}
在React Native層,通過NativeModules呼叫commModule,繼而呼叫原生方法即可。注意:commModule要與Module類的getNames方法返回的名稱對應。
2. 接收原生呼叫
/**
* 接收原生呼叫
*/
componentDidMount() {
DeviceEventEmitter.addListener('nativeCallRn',(msg)=>{
title = "React Native介面,收到資料:" + msg;
ToastAndroid.show("傳送成功", ToastAndroid.SHORT);
})
}
通過DeviceEventEmitter註冊監聽,類似於Android中的監聽事件。第一個引數標識名稱,要與Module中emit的Event Name相同。第二個引數即為處理回掉。
3.介面程式碼
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome} >
{title}
</Text>
<Text style={styles.welcome} onPress={this.skipNativeCall.bind(this)}>
跳轉到撥號介面
</Text>
<Image source={require('./images/ic.png')} />
</View>
);
}
在Text中註冊單擊事件,RN層呼叫原生程式碼,跳轉到撥號介面。
4.Android層呼叫RN的程式碼
/**
* 向RN傳送訊息
* @param v
*/
public void sendMsgToRN(View v) {
Log.e("---","sendMsgToRN");
MainApplication.getReactPackage().mModule.nativeCallRn("hello");
}
呼叫Module中定義的nativeCallRn方法,繼而出發RN層程式碼。以上就是通過 RCTDeviceEventEmitter 模式進行通訊互動。可以很清晰的看出,互動都是以主動方式為主。
RN中剩下的兩種通訊方式,存在一個共同的特點:
從RN層呼叫Native層,Native層處理完成後,回撥RN層
直接看程式碼實現:
(1)Callback
同樣還是在Module類中定義互動方法:
/**
* Callback 方式
* rn呼叫Native,並獲取返回值
* @param msg
* @param callback
*/
@ReactMethod
public void rnCallNativeFromCallback(String msg, Callback callback) {
// 1.處理業務邏輯...
String result = "處理結果:" + msg;
// 2.回撥RN,即將處理結果返回給RN
callback.invoke(result);
}
RN中定義回撥:
/**
* Callback 通訊方式
*/
callbackComm(msg) {
NativeModules.commModule.rnCallNativeFromCallback(msg,(result) => {
ToastAndroid.show("CallBack收到訊息:" + result, ToastAndroid.SHORT);
})
}
(2)Promise
Module類中定義互動方法:
/**
* Promise 方式
* @param msg
* @param promise
*/
@ReactMethod
public void rnCallNativeFromPromise(String msg, Promise promise) {
Log.e("---","adasdasda");
// 1.處理業務邏輯...
String result = "處理結果:" + msg;
// 2.回撥RN,即將處理結果返回給RN
promise.resolve(result);
}
RN中定義回撥:
/**
* Promise 通訊方式
*/
promiseComm(msg) {
NativeModules.commModule.rnCallNativeFromPromise(msg).then(
(result) =>{
ToastAndroid.show("Promise收到訊息:" + result, ToastAndroid.SHORT)
}
).catch((error) =>{console.log(error)});
}
佈局中觸發單擊事件:
<Text style={styles.welcome} onPress={this.skipNativeCall.bind(this)}>
跳轉到撥號介面
</Text>
<Text style={styles.welcome} onPress={this.callbackComm.bind(this,'callback傳送啦')}>
Callback通訊方式
</Text>
<Text style={styles.welcome} onPress={this.promiseComm.bind(this,'promise傳送啦')}>
Promise通訊方式
</Text>
(3)直傳常量資料(原生向RN)
自定義Module類中實現getConstants方法
@Nullable
@Override
public Map<String, Object> getConstants() {
return super.getConstants();
}
以上是預設實現,getConstants方法中呼叫了super.getConstants(),跟蹤原始碼可以看到
/**
* @return a map of constants this module exports to JS. Supports JSON types.
*/
public @Nullable Map<String, Object> getConstants() {
return null;
}
從原始碼註釋中可以看出,該方法是返回一個Map型別的常量,匯出到JS端(即RN)。支援JSON 型別。所以,我們只需要重寫方法,宣告Map集合,向其中新增常量後,返回即可。所以就有了如下程式碼
@Nullable
@Override
public Map<String, Object> getConstants() {
Map<String,Object> params = new HashMap<>();
params.put("Constant","我是常量,傳遞給RN");
return params;
}
此時,在RN端,我們可以通過NativeModules來接收即可
componentWillMount() {
let result = NativeModules.MyModule.Constant
}
最終效果:
Android呼叫React Native:
React Native呼叫Android:
原始碼以上傳到github,希望大家star,follow支援哦~