前言
最近在在學(xué)習(xí)flutter語言流炕,發(fā)現(xiàn)flutter需要跟原生混合開發(fā)赁遗,混合開發(fā)中其實原生很多框架代碼都已經(jīng)寫好了曼追,比如網(wǎng)絡(luò)框架顽决,或者想要調(diào)用原生系統(tǒng)的東西短条,比如獲取手機電量啊,其實flutter是直接可以調(diào)用native端的代碼才菠,可以跟原生通信茸时,通信的名詞稱為:Platform Channels(平臺通道)。Flutter平臺特定的API支持不依賴于代碼生成赋访,而是依賴于靈活的消息傳遞的方式:應(yīng)用的Flutter部分通過平臺通道(platform channel)將消息發(fā)送到其應(yīng)用程序的所在的宿主(iOS或Android)可都。
宿主監(jiān)聽的平臺通道,并接收該消息蚓耽。然后它會調(diào)用特定于該平臺的API(使用原生編程語言) - 并將響應(yīng)發(fā)送回客戶端渠牲,即應(yīng)用程序的Flutter部分
框架概述: 平臺通道
使用平臺通道在客戶端(Flutter UI)和宿主(平臺)之間傳遞消息,如下圖所示:
上圖中用到了MethodChannel田晚,其實flutter是有三種通信類型嘱兼,分別是:
* BasicMessageChannel:用于傳遞字符串和半結(jié)構(gòu)化的信息,這個用的比較少
* MethodChannel:用于傳遞方法調(diào)用(method invocation)通常用來調(diào)用native中某個方法
* EventChannel: 用于數(shù)據(jù)流(event streams)的通信。有監(jiān)聽功能贤徒,比如電量變化之后直接推送數(shù)據(jù)給flutter端芹壕。
三種Channel之間互相獨立,各有用途接奈,但它們在設(shè)計上卻非常相近踢涌。每種Channel均有三個重要成員變量:
* name: String類型,代表Channel的名字序宦,也是其唯一標(biāo)識符睁壁。
* messager:BinaryMessenger類型,代表消息信使互捌,是消息的發(fā)送與接收的工具潘明。
* codec: MessageCodec類型或MethodCodec類型,代表消息的編解碼器秕噪。
注意:以上圖片箭頭是雙向的钳降,也就是flutter和native是可以相互調(diào)用通信。
具體原理深度理解可以看這篇文章:channel原理篇
channel通信的數(shù)據(jù)類型
雙方通信傳遞數(shù)據(jù)只能是如下類型數(shù)據(jù)腌巾,你不能傳遞一個自己建立的javabean過去遂填。
具體使用
1.MethodChannel的使用
1.1-Native端寫法
這個代碼是我封裝了一個HttpMethodChannel類铲觉,用于調(diào)用原生網(wǎng)絡(luò)框架的,實現(xiàn)MethodCallHandler吓坚,重寫onMethodCall方法撵幽,在構(gòu)造方法里面new一個MethodChannel類,需要傳遞兩個參數(shù)礁击,一個是FlutterView盐杂,以為底層原理其實就是通過FlutterNativeView調(diào)用JNI方法把數(shù)據(jù)轉(zhuǎn)換成二進(jìn)制數(shù)據(jù)傳遞給flutter的。第二個參數(shù)是一個唯一的字符串HTTP_CHANNEL客税,到時flutter端也需要使用這個字符串况褪。可以直接考過去更耻。
接著httpChannel.setMethodCallHandler(this)把httpMethodChannel傳進(jìn)去测垛。
/**
* 構(gòu)造網(wǎng)絡(luò)通信渠道
* Created by wuminjian on 2018/12/13.
*/
public class HttpMethodChannel implements MethodChannel.MethodCallHandler {
private static final String TAG = "HttpMethodChannel";
private static final String HTTP_CHANNEL = "com.lingan.seeyou/http";
private MethodChannel httpChannel;
private Context context;
private HttpMethodChannel(Context context, FlutterView flutterView) {
this.context = context;
httpChannel = new MethodChannel(flutterView, HTTP_CHANNEL);
httpChannel.setMethodCallHandler(this);
}
/**
* 暴露到外面的靜態(tài)create類
*/
public static HttpMethodChannel create(Context context, FlutterView flutterView) {
return new HttpMethodChannel(context, flutterView);
}
@Override
public void onMethodCall(MethodCall methodCall,MethodChannel.Result result) {
String url = null;
String param;
if (methodCall.hasArgument(HttpConstant.HTTP_URL)) {
url = methodCall.argument(HttpConstant.HTTP_URL);
}
if (methodCall.hasArgument(HttpConstant.HTTP_PARAMS)) {
param = methodCall.argument(HttpConstant.HTTP_PARAMS);
}
if (TextUtils.isEmpty(url)) {
result.success("");
return;
}
switch (methodCall.method) {
//get請求
case HTTP_GET:
HttpController.getInstance().httpGet(url, null,result);
break;
//post請求
case HTTP_POST:
ToastUtils.showToast(context, "post網(wǎng)絡(luò)請求");
result.success("我準(zhǔn)備開始post請求了");
break;
}
}
- 接下來我們看看onMethodCall方法,在Flutter發(fā)送請求時秧均, onMethodCall
方法會執(zhí)行食侮。onMethodCall有兩個入?yún)ⅲ?MethodCall 中有關(guān)當(dāng)前請求的信息,例如調(diào)用方法的名字HTTP_GET這個變量“get”其實就是flutter那邊傳過來的方法名字告訴我它需要請求的是網(wǎng)絡(luò)的get請求操作目胡,methodCall.argument是獲取flutter那邊傳遞過來的其他參數(shù)數(shù)據(jù)锯七,比如網(wǎng)絡(luò)請求中的url、headers誉己、boday等數(shù)據(jù)眉尸。
-
Result對象是回調(diào)數(shù)據(jù)給flutter中使用的,有三個方法:
1. result.success(data); 成功的時候巨双,返回數(shù)據(jù)調(diào)用 2. result.error() 失敗的情況下噪猾,調(diào)用 3. result.notImplemented(); 這個是代表比如:我上面的列子中HTTP_GET方法沒有實現(xiàn)的話,可以告訴flutter方法沒有實現(xiàn)
1.2Flutter端寫法
class ListWigetState extends State<ListWiget> {
List subjects = [];
MethodChannel platform = const MethodChannel(ChannelUtils.HTTP_CHANNEL);
@override
void initState() {
loadNativeData();
}
首頁也是構(gòu)造一個MethodChannel對象筑累,里面的參數(shù)就是我們在原生那邊定個的字符串袱蜡,記住一定是要一樣的。
-
接下來就是用MethodChannel對象調(diào)用的代碼:
/** * 加載原生網(wǎng)絡(luò)請求 */ loadNativeData() async { var responseBody; try { var params = {"a": 1}; responseBody = await platform.invokeMethod("GET",{"url": HttpApi.HTTP_HOME_URL, "params": params.toString(),"headers":null}); print("zzzz: "+responseBody); var convertDataToJson = jsonDecode(responseBody)["subjects"]; setState(() { subjects = convertDataToJson; }); } on PlatformException catch (e) { print(e.toString()); } }
responseBody = await platform.invokeMethod("GET",{"url": HttpApi.HTTP_HOME_URL, "params": params.toString(),"headers":null});
這行代碼就是通過通道來調(diào)用Native方法了慢宗。注意這里的await關(guān)鍵字是異步的坪蚁,所以這里必須要使用await關(guān)鍵字,"GET"就是我們之前提的傳遞的方法名字,代表是get請求還是post請求镜沽,{"url": HttpApi.HTTP_HOME_URL, "params": params.toString(),"headers":null}這個是我們需要傳遞過去的參數(shù)敏晤。
在上面Native代碼中我們把獲取到的數(shù)據(jù)是通過result.success();返回給Flutter。這里await表達(dá)式執(zhí)行完成以后就直接賦值給responseBody變量了缅茉。
1.3雙向調(diào)用通信
上面我們說的是flutter調(diào)用Native端的用法茵典,看最上面的圖我們知道其實都雙向的調(diào)用,即Native端也可以調(diào)用flutter的.
-
舉個例子宾舅,我們想從Native端請求Flutter端的一個getFlutterName方法獲取一個字符串统阿。在Flutter端你需要給MethodChannel設(shè)置一個MethodCallHandler:
platform.setMethodCallHandler(platformCallHandler); Future<dynamic> platformCallHandler(MethodCall call) async { switch (call.method) { case "getFlutterName": return "Flutter name flutter"; break; } }
-
在Native端,只需要讓對應(yīng)的的channel調(diào)用invokeMethod就行了:
channel.invokeMethod("getFlutterName", null, new MethodChannel.Result() { @Override public void success(Object o) { // 這里就會輸出 "Flutter name flutter" Log.i("debug", o.toString()); } @Override public void error(String s, String s1, Object o) { } @Override public void notImplemented() { } });
上圖中的三個方法就是我前面提到的三個方法“成功”筹我、“失敗”扶平、“沒有實現(xiàn)”的三個方法的回調(diào)。其實說白了就是反過來調(diào)用而已蔬蕊。
2.EventChannel的使用
EventChannel的使用我們也以官方獲取電池電量的demo為例结澄,手機的電池狀態(tài)是不停變化的。我們要把這樣的電池狀態(tài)變化由Native及時通過EventChannel來告訴Flutter岸夯。這種情況用之前講的MethodChannel辦法是不行的麻献,這意味著Flutter需要用輪詢的方式不停調(diào)用getBatteryLevel來獲取當(dāng)前電量,顯然是不正確的做法猜扮。而用EventChannel的方式勉吻,則是將當(dāng)前電池狀態(tài)"推送"給Flutter.
2.1EventChannel-Native端寫法
先看我們熟悉的Native端怎么來創(chuàng)建EventChannel, 還是封裝一個FlutterEventChannel類,然后在MainActivity.onCreate中調(diào)用FlutterEventChannel的create方法把FlutterView傳進(jìn)來,代碼如下:
public class FlutterEventChannel implements EventChannel.StreamHandler {
private static final String TAG = "FlutterEventChannel";
private static final String EVENT_CHANNEL_NAME = "com.meetyou.flutter/event";
private FlutterEventChannel(FlutterView flutterView) {
EventChannel eventChannel = new EventChannel(flutterView, EVENT_CHANNEL_NAME);
eventChannel.setStreamHandler(this);
}
public static FlutterEventChannel create(FlutterView flutterView) {
return new FlutterEventChannel(flutterView);
}
private EventChannel.EventSink eventSink;
/**
* 暴露出去供界面?zhèn)鲾?shù)據(jù)到Flutter
*/
public void sendEvent(Object data) {
if (eventSink != null) {
eventSink.success(data);
} else {
LogUtils.e(TAG, "===== FlutterEventChannel.eventSink 為空 需要檢查一下 =====");
}
}
@Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
this.eventSink = eventSink;
}
@Override
public void onCancel(Object o) {
eventSink = null;
}
}
和MethodChannel類似旅赢,我們也是直接new一個EventChannel實例齿桃,并給它設(shè)置了一個StreamHandler類型的回調(diào)。其中onCancel代表對面不再接收煮盼,這里我們應(yīng)該做一些clean up的事情短纵。而 onListen則代表通道已經(jīng)建好,Native可以發(fā)送數(shù)據(jù)了僵控。注意onListen里帶的EventSink這個參數(shù)香到,后續(xù)Native發(fā)送數(shù)據(jù)都是經(jīng)過EventSink的”ㄆ疲看代碼:
private BroadcastReceiver createChargingStateChangeReceiver(final EventSink events) {
return new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
if (status == BatteryManager.BATTERY_STATUS_UNKNOWN) {
events.error("UNAVAILABLE", "Charging status unavailable", null);
} else {
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// 把電池狀態(tài)發(fā)給Flutter
events.success(isCharging ? "charging" : "discharging");
}
}
};
}
上面的代碼就是監(jiān)聽了點亮改變的廣播悠就,在onReceive函數(shù)內(nèi),系統(tǒng)發(fā)來電池狀態(tài)廣播以后泛烙,在Native這里轉(zhuǎn)化為約定好的字符串理卑,然后通過調(diào)用events.success();發(fā)送給Flutter。Native端的代碼就是這樣蔽氨,接下來看Flutter端藐唠。
2.2EventChannel-Flutter端寫法
首先還是在State內(nèi)創(chuàng)建EventChannel,然后在initState的時候打開這個channel:
static const EventChannel eventChannel =
const EventChannel('com.meetyou.flutter/event');
@override
void initState() {
super.initState();
eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
}
收到event以后的處理是在_onEvent函數(shù)里:
void _onEvent(Object event) {
setState(() {
_chargingStatus =
"Battery status: ${event == 'charging' ? '' : 'dis'}charging.";
});
}
void _onError(Object error) {
setState(() {
_chargingStatus = 'Battery status: unknown.';
});
}
從Native端傳過來的"charging"/"discharging"字符串直接就是入?yún)vent.