作為一個(gè)UI框架,F(xiàn)lutter提供了三種通道來(lái)和原生平臺(tái)通信。
- BasicMessageChannel:它提供類似于BinaryMessages的基本消息傳遞服務(wù),但可自定義消息編解碼器,支持發(fā)送字符串或半結(jié)構(gòu)化消息。
- MethodChannel:它使用異步方法調(diào)用的方式進(jìn)行平臺(tái)通信。
- EventChannel:它使用事件流的方式進(jìn)行平臺(tái)通信。

三種方式的設(shè)計(jì)是非常相似的,分別維護(hù)了兩個(gè)成員屬性:
- name:用來(lái)標(biāo)識(shí)通道。
- codec:消息編解碼器。
另外,三種方式其實(shí)都是通過(guò)BinaryMessages來(lái)進(jìn)行消息的傳遞,它負(fù)責(zé)將二進(jìn)制消息發(fā)送到平臺(tái)插件并從平臺(tái)插件接收二進(jìn)制消息。最后通過(guò)消息編解碼器來(lái)將二進(jìn)制信息轉(zhuǎn)換成我們需要的數(shù)據(jù)類型,注意三種通信方式都是雙向的。以BasicMessageChannel為例:
Future<T> send(T message) async {
return codec.decodeMessage(await BinaryMessages.send(name, codec.encodeMessage(message)));
}
細(xì)心的同學(xué)會(huì)發(fā)現(xiàn),圖上還有一個(gè)OptionalMethodChannel,這并不是一種新的通信方式,而是MethodChannel的進(jìn)一步封裝,如果找不到對(duì)應(yīng)插件,返回的是null,而不再拋出MissingPluginException異常。
class OptionalMethodChannel extends MethodChannel{
...
@override
Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
try {
final T result = await super.invokeMethod<T>(method, arguments);
return result;
} on MissingPluginException {
return null;
}
}
...
}
BasicMessageChannel
使用介紹


三種通道分別在flutter和其他平臺(tái)都提供了相關(guān)實(shí)現(xiàn),并提供了相似的api,這里以BasicMessageChannel為例展示一下。
成員屬性
- name:通道名字,要保證通信的通道在flutter和native端保持一致。
- codec:消息的編解碼器,同樣要保證通信的通道在flutter和native端保持一致。
- messenger:消息的發(fā)送器,類似于flutter中的BinaryMessages。
Api接口
發(fā)送消息
flutter端:可以看到發(fā)送消息是個(gè)異步方法,大概執(zhí)行順序就是先將數(shù)據(jù)編碼成字節(jié)數(shù)據(jù),通過(guò)BinaryMessages傳輸,等待native返回?cái)?shù)據(jù),再解碼成我們需要的數(shù)據(jù),如果native沒(méi)有返數(shù)據(jù),則Future為null。
Future<T> send(T message) async {
return codec.decodeMessage(await BinaryMessages.send(name, codec.encodeMessage(message)));
}
android端:其實(shí)實(shí)現(xiàn)是類似的,只不過(guò)不是使用Future,而是用回調(diào)的方式來(lái)監(jiān)聽(tīng)返回?cái)?shù)據(jù)。
public void send(T message) {
this.send(message, (BasicMessageChannel.Reply)null);
}
public void send(T message, BasicMessageChannel.Reply<T> callback) {
this.messenger.send(this.name, this.codec.encodeMessage(message), callback == null ? null : new BasicMessageChannel.IncomingReplyHandler(callback));
}
接收消息
flutter端:我們可以看到傳入的參數(shù)是一個(gè)異步函數(shù),handler接收的參數(shù)為native平臺(tái)發(fā)送的數(shù)據(jù),handler的返回值將作為響應(yīng)返回給native端。
void setMessageHandler(Future<T> handler(T message)) {
if (handler == null) {
BinaryMessages.setMessageHandler(name, null);
} else {
BinaryMessages.setMessageHandler(name, (ByteData message) async {
return codec.encodeMessage(await handler(codec.decodeMessage(message)));
});
}
}
android端:同樣是通過(guò)接口回調(diào)的方式來(lái)接收和響應(yīng)消息的傳遞。
public void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler) {
this.messenger.setMessageHandler(this.name, handler == null ? null : new BasicMessageChannel.IncomingMessageHandler(handler));
}
public interface MessageHandler<T> {
void onMessage(T message, BasicMessageChannel.Reply<T> reply);
}
示例代碼
我們來(lái)做這樣一個(gè)demo,flutter發(fā)送一條消息到native,native收到消息后給個(gè)回復(fù),并發(fā)送一條新的消息到flutter,flutter收到后再回復(fù)給native。
flutter:
PluginChannel.listenBasicMessage();
RaisedButton(
onPressed: () {
PluginChannel.sendBasicMessage();
},
child: Text("BasicMessageChannel"),
)
...
class PluginChannel {
static const _basicMessageChannelName = "study_3/basicMessageChannel";
static const _basicMessageChannel = BasicMessageChannel(_basicMessageChannelName, StandardMessageCodec());
static void listenBasicMessage(){
_basicMessageChannel
.setMessageHandler((result) async{
print('flutter listen:$result');
return "flutter response to native";
});
}
static void sendBasicMessage() {
_basicMessageChannel
.send("flutter send to native")
.then((result) {
print('flutter receive response:$result');
});
}
}
android:
private val BASIC_MESSAGE_CHANNEL = "study_3/basicMessageChannel"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
basicMessageChanelDemo()
}
private fun basicMessageChanelDemo(){
BasicMessageChannel(this.flutterView,BASIC_MESSAGE_CHANNEL, StandardMessageCodec.INSTANCE)
.setMessageHandler { any, reply ->
println("android listen:$any")
reply.reply("android response to flutter")
BasicMessageChannel(this.flutterView,BASIC_MESSAGE_CHANNEL, StandardMessageCodec.INSTANCE)
.send("android send to flutter"){
println("android receive response:$it")
}
}
}
日志:
I/System.out(27557): android listen:flutter send to native
I/flutter (27557): flutter receive response:android response to flutter
I/flutter (27557): flutter listen:android send to flutter
I/System.out(27557): android receive response:flutter response to native
MethodChannel
一些想法
MethodChannel通過(guò)傳遞方法名和參數(shù),來(lái)達(dá)到通信的效果,給我的感覺(jué)和BasicMessageChannel并沒(méi)有本質(zhì)上的不同,使用BasicMessageChannel傳遞一個(gè)數(shù)據(jù),做一系列操作再返回?cái)?shù)據(jù),給我的感覺(jué)效果是一樣的。為了驗(yàn)證自己的想法,大概翻了一下源碼:
首先看兩者的發(fā)送數(shù)據(jù)方法:
///BasicMessageChannel
Future<T> send(T message) async {
return codec.decodeMessage(await BinaryMessages.send(name, codec.encodeMessage(message)));
}
///MethodChannel
Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
assert(method != null);
final ByteData result = await BinaryMessages.send(
name,
codec.encodeMethodCall(MethodCall(method, arguments)),
);
if (result == null) {
throw MissingPluginException('No implementation found for method $method on channel $name');
}
final T typedResult = codec.decodeEnvelope(result);
return typedResult;
}
兩者內(nèi)部的實(shí)現(xiàn)邏輯基本上是一樣的,編碼成二進(jìn)制、BinaryMessages發(fā)送、解碼。都是調(diào)用的BinaryMessages的send方法,唯一區(qū)別就是編解碼器codec的不同。下面來(lái)看看codec的實(shí)現(xiàn):
///BasicMessageChannel StandardMessageCodec
ByteData encodeMessage(dynamic message) {
if (message == null)
return null;
final WriteBuffer buffer = WriteBuffer();
writeValue(buffer, message);
return buffer.done();
}
///MethodChannel StandardMethodCodec
const StandardMethodCodec([this.messageCodec = const StandardMessageCodec()]);
ByteData encodeMethodCall(MethodCall call) {
final WriteBuffer buffer = WriteBuffer();
messageCodec.writeValue(buffer, call.method);
messageCodec.writeValue(buffer, call.arguments);
return buffer.done();
}
我們可以看到StandardMethodCodec中的messageCodec默認(rèn)為StandardMessageCodec,而且編碼方法和StandardMessageCodec的編碼方法是一樣的。默認(rèn)實(shí)現(xiàn)就是相當(dāng)于把方法名和參數(shù)封裝成了一個(gè)MethodCall對(duì)象,再通過(guò)BasicMessageChannel傳遞。
Api
//發(fā)送
Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {}
//接收
void setMethodCallHandler(Future<dynamic> handler(MethodCall call)) {}
Api不再贅述,設(shè)計(jì)和使用都類似于BasicMessageChannel,發(fā)送的時(shí)候傳入方法名和參數(shù),返回一個(gè)Future來(lái)監(jiān)聽(tīng)響應(yīng)。接收的時(shí)候傳入一個(gè)高階函數(shù),參數(shù)為收到的信息,返回值為要響應(yīng)的數(shù)據(jù)。
示例代碼
依然是一個(gè)flutter和android相互調(diào)用的demo。
flutter:
PluginChannel.listenMethod();
onPressed: () {
PluginChannel.sendBasicMessage();
},
class PluginChannel {
static const _methodChannelName = "study_3/methodChannel";
static const _methodChannel = MethodChannel(_methodChannelName);
static void invokeMethod() {
_methodChannel.invokeMethod("getAge", {"name": "lili"}).then((result) {
print('flutter receive response:$result');
});
}
static void listenMethod() {
_methodChannel.setMethodCallHandler((methodCall) async {
print('flutter listen:$methodCall');
return "男";
});
}
}
android:
private val METHOD_CHANNEL = "study_3/methodChannel"
override fun onCreate(savedInstanceState: Bundle?) {
methodChannelDemo()
}
private fun methodChannelDemo(){
MethodChannel(this.flutterView,METHOD_CHANNEL)
.setMethodCallHandler { methodCall, result ->
println("android listen:${methodCall.method} \t ${methodCall.arguments}")
when(methodCall.method){
"getAge" -> {
result.success(getAge(methodCall.argument<String>("name")))
}
}
MethodChannel(this.flutterView,METHOD_CHANNEL)
.invokeMethod("getSex", mapOf(Pair("name","tom")), object : MethodChannel.Result {
override fun notImplemented() {
println("android receive notImplemented")
}
override fun error(p0: String?, p1: String?, p2: Any?) {
println("android receive error")
}
override fun success(p0: Any?) {
println("android receive response:$p0")
}
})
}
}
private fun getAge(name:String?): Int{
return when(name){
"lili" -> 18
"tom" -> 19
"allen" -> 20
else -> 0
}
}
日志:
I/System.out( 9700): android listen:getAge {name=lili}
I/flutter ( 9700): flutter receive response:18
I/flutter ( 9700): flutter listen:MethodCall(getSex, {name: tom})
I/System.out( 9700): android receive response:男
EventChannel
Api
EventChannel并沒(méi)有分別提供發(fā)送和收聽(tīng)消息的方法,它只提供了一個(gè)receiveBroadcastStream方法,用于發(fā)送消息,同時(shí)返回一個(gè)流(Stream),用于監(jiān)聽(tīng)平臺(tái)插件成功返回的所有事件信息,這個(gè)流可以被監(jiān)聽(tīng)不止一次。因此我們可以用于native端需要持續(xù)傳遞數(shù)據(jù)到flutter的情況,比如監(jiān)聽(tīng)電量,調(diào)用攝像頭等等。
Stream<dynamic> receiveBroadcastStream([dynamic arguments]) {
final MethodChannel methodChannel = MethodChannel(name, codec);
StreamController<dynamic> controller;
controller = StreamController<dynamic>.broadcast(onListen, onCancel);
return controller.stream;
}
上面是receiveBroadcastStream的抽象代碼,總共做了兩件事:
- 創(chuàng)建一個(gè)MethodChannel,傳入自己的name和codec屬性。
- 創(chuàng)建一個(gè)StreamController,并返回流。
StreamController.broadcast是一個(gè)命名構(gòu)造函數(shù),它返回一個(gè)廣播流,可以不止一次被監(jiān)聽(tīng),它是惰性的,在首次被訂閱時(shí)調(diào)用onListen,不再訂閱時(shí)調(diào)用onCancel。如果之后繼續(xù)訂閱,則再次調(diào)用onListen。
那么我們來(lái)繼續(xù)看下receiveBroadcastStream中的onListen做了什么:
BinaryMessages.setMessageHandler(name, (ByteData reply) async {
if (reply == null) {
controller.close();
} else {
try {
controller.add(codec.decodeEnvelope(reply));
} on PlatformException catch (e) {
controller.addError(e);
}
}
return null;
});
try {
await methodChannel.invokeMethod<void>('listen', arguments);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: 'while activating platform stream on channel $name',
));
}
可以看到onListen中依然是做了兩件事情:
- 設(shè)置回調(diào)來(lái)接收平臺(tái)插件返回的消息。
- 通過(guò)之前創(chuàng)建的MethodChannel發(fā)送一條消息,方法名定死為listen,參數(shù)為receiveBroadcastStream傳入的可選參數(shù)。
最后再來(lái)看下receiveBroadcastStream中的onCancel:
BinaryMessages.setMessageHandler(name, null);
try {
await methodChannel.invokeMethod<void>('cancel', arguments);
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'services library',
context: 'while de-activating platform stream on channel $name',
));
}
依然兩件事:
- 移除這個(gè)通道。
- 用之前創(chuàng)建的MethodChannel發(fā)送一條消息,方法名定死為cancel,參數(shù)為receiveBroadcastStream傳入的可選參數(shù)。
可以想象,native平臺(tái)肯定也定義了一個(gè)MethodChannel,用來(lái)接收l(shuí)isten和cancel方法,我們來(lái)驗(yàn)證一下,以Android端為例,EventChannel部分源碼:
public void onMessage(ByteBuffer message, BinaryReply reply) {
MethodCall call = EventChannel.this.codec.decodeMethodCall(message);
if (call.method.equals("listen")) {
this.onListen(call.arguments, reply);
} else if (call.method.equals("cancel")) {
this.onCancel(call.arguments, reply);
} else {
reply.reply((ByteBuffer)null);
}
}
我們可以看到,當(dāng)收到消息的時(shí)候,有三種情況:
- 收到listen方法,則調(diào)用onListen。
- 收到cancel方法,則調(diào)用onCancel。
- 其他,則返回null,此時(shí)flutter端收到null則會(huì)關(guān)閉這個(gè)流。
示例代碼
flutter:
class PluginChannel {
static const _eventChannelName = "study_3/eventChannel";
static const _eventChannel = EventChannel(_eventChannelName);
static void event() {
_eventChannel.receiveBroadcastStream("event arg")
.listen((result) {
print('flutter listen:$result');
});
}
}
android:
private fun eventChannelDemo(){
EventChannel(this.flutterView,EVENT_CHANNEL)
.setStreamHandler(object : EventChannel.StreamHandler {
override fun onListen(p0: Any?, events: EventChannel.EventSink?) {
println("android onListen:$p0")
events?.success(1)
events?.success(2)
events?.success(3)
events?.success(4)
events?.endOfStream()
events?.success(5)
}
override fun onCancel(p0: Any?) {
println("android onCancel:$p0")
}
})
}
日志:
I/System.out( 9271): android onListen:event arg
I/flutter ( 9271): flutter listen:1
I/flutter ( 9271): flutter listen:2
I/flutter ( 9271): flutter listen:3
I/flutter ( 9271): flutter listen:4
I/System.out( 9271): android onCancel:event arg