您好,登錄后才能下訂單哦!
Android與Flutter之間怎么實現通信,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
1、架構概述
消息通過平臺通道在native(host)與flutter(client)之間傳遞,如下圖所示:
為了確保用戶界面能夠正確響應,消息都是以異步的方式進行傳遞。無論是native向flutter發送消息,還是flutter向native發送消息。
在flutter中,MethodChannel可以發送與方法調用相對應的消息。在native平臺上,MethodChannel在Android可以接收方法調用并返回結果。這些類可以幫助我們用很少的代碼就能開發平臺插件。
注意:本節內容來自flutter官網,讀者可自行查閱。
2、平臺通道數據類型支持和編解碼器
平臺通道可以使用提供的編解碼器對消息進行編解碼,這些編解碼器支持簡單類似JSON的值的高效二進制序列化,例如布爾值,數字,字符串,字節緩沖區以及這些的列表和映射。當你發送和接收值時,會自動對這些值進行序列化和反序列化。
下表顯示了如何在平臺端接收Dart值,反之亦然:
關于編解碼器,Android端提供了以下幾種。
BinaryCodec:是最簡單的一種編解碼器,其返回值類型與入參的類型相同,均為二進制格式(ByteBuffer)。由于BinaryCodec在編解碼過程中什么都沒做,只是原封不動的將二進制數據返回。所以傳遞的數據在編解碼時會免于拷貝,這種方式在傳遞的數據量比較大時很有用。比如從Android側傳入一張圖片到Flutter側顯示。
StandardMessageCodec:是BasicMessageChannel的默認編解碼器,支持基礎數據類型、列表及字典等。在編碼時會先將數據寫入到ByteArrayOutputStream流中,然后再將該流中的數據寫入到ByteBuffer中。在解碼時,直接從ByteBuffer中讀取數據。
StandardMethodCodec:是基于StandardMessageCodec的封裝。是MethodChannel與EventChannel的默認編解碼器。
StringCodec:是用于字符串與二進制數據之間的編解碼,其編碼格式為UTF-8。在編碼時會將String轉成byte數組,然后再將該數組寫入到ByteBuffer中。在解碼時,直接從ByteBuffer中讀取數據
JSONMessageCodec:內部調用StringCodec來實現編解碼。
JSONMethodCodec:基于JSONMessageCodec的封裝。可以在MethodChannel與EventChannel中使用。
ByteBuffer是Nio中的一個類,顧名思義——就是一塊存儲字節的區域。它有兩個實現類——DirectByteBuffer與HeapByteBuffer,DirectByteBuffer是直接在內存中開辟了一塊區域來存儲數據,而HeapByteBuffer是在JVM堆中開辟一塊區域來存儲數據,所以要想數據在DirectByteBuffer中與HeapByteBuffer互通,就需要進行一次拷貝。
3、通信方式
前面講了Android與flutter通信的一些基礎知識,下面就進入正題,來看Android如何與flutter進行通信。
Android與Flutter之間的通信共有四種實現方式。
由于在初始化flutter頁面時會傳遞一個字符串——route,因此我們就可以拿route來做文章,傳遞自己想要傳遞的數據。該種方式僅支持單向數據傳遞且數據類型只能為字符串,無返回值。
通過EventChannel來實現,EventChannel僅支持數據單向傳遞,無返回值。
通過MethodChannel來實現,MethodChannel支持數據雙向傳遞,有返回值。
通過BasicMessageChannel來實現,BasicMessageChannel支持數據雙向傳遞,有返回值。
下面就來看一下這幾種方式的使用。
3.1、初始化時傳值
主要是利用了創建flutter頁面傳遞的route來做文章,筆者認為該種方式屬于取巧,但還是可以用來傳遞數據。它的使用很簡單,代碼如下。
首先來看Android代碼。
//第三個參數可以換成我們想要字符串。 FlutterView flutterView = Flutter.createView(this, getLifecycle(), "route");
在flutter中,我們只需要通過下面代碼來獲取值即可。
void main() => runApp(MyApp( initParams: window.defaultRouteName, )); class MyApp extends StatelessWidget { final String initParams;//既是前面傳遞的值——route MyApp({Key key, @required this.initParams}) : super(key: key); @override Widget build(BuildContext context) {...} }
通過該種方式就可以在初始化flutter時,Android給flutter傳遞數據。由于runApp僅會調用一次,所以該種方式只能傳遞一次數據且數據只能是字符串。
使用window的相關API需要導入包dart:ui
3.2、EventChannel
EventChannel是一種native向flutter發送數據的單向通信方式,flutter無法返回任何數據給native。主要用于native向flutter發送手機電量變化、網絡連接變化、陀螺儀、傳感器等。它的使用方式如下。
首先來看Android代碼。
public class EventChannelPlugin implements EventChannel.StreamHandler { private static final String TAG = EventChannelPlugin.class.getSimpleName(); private EventChannel.EventSink eventSink; private Activity activity; static EventChannelPlugin registerWith(FlutterView flutterView) { EventChannelPlugin plugin = new EventChannelPlugin(flutterView); new EventChannel(flutterView, "EventChannelPlugin").setStreamHandler(plugin); return plugin; } private EventChannelPlugin(FlutterView flutterView) { this.activity = (Activity) flutterView.getContext(); } void send(Object params) { if (eventSink != null) { eventSink.success(params); } } void sendError(String str1, String str2, Object params) { if (eventSink != null) { eventSink.error(str1, str2, params); } } void cancel() { if (eventSink != null) { eventSink.endOfStream(); } } //第一個參數為flutter初始化EventChannel時返回的值,僅此一次 @Override public void onListen(Object o, EventChannel.EventSink eventSink) { this.eventSink = eventSink; Log.i(TAG, "eventSink:" + eventSink); Log.i(TAG, "Object:" + o.toString()); Toast.makeText(activity, "onListen——obj:" + o, Toast.LENGTH_SHORT).show(); } @Override public void onCancel(Object o) { Log.i(TAG, "onCancel:" + o.toString()); Toast.makeText(activity, "onCancel——obj:" + o, Toast.LENGTH_SHORT).show(); this.eventSink = null; } }
筆者對Android端代碼做了一個簡單的封裝,還是很好理解的。下面就來看flutter代碼實現。
class _MyHomePageState extends State<MyHomePage> { EventChannel _eventChannelPlugin = EventChannel("EventChannelPlugin"); StreamSubscription _streamSubscription; @override void initState() { _streamSubscription = _eventChannelPlugin //["abc", 123, "你好"]對應著Android端onListen方法的第一個參數,可不傳值 .receiveBroadcastStream(["abc", 123, "你好"]) .listen(_onToDart, onError: _onToDartError, onDone: _onDone); super.initState(); } @override void dispose() { if (_streamSubscription != null) { _streamSubscription.cancel(); _streamSubscription = null; } super.dispose(); } //native端發送正常數據 void _onToDart(message) { print(message); } //當native出錯時,發送的數據 void _onToDartError(error) { print(error); } //當native發送數據完成時調用的方法,每一次發送完成就會調用 void _onDone() { print("消息傳遞完畢"); } @override Widget build(BuildContext context) {...} }
上面就是通過EventChannel來進行通信的代碼實現,調用EventChannelPlugin的send方法就能給flutter發送數據。
3.3、MethodChannel
MethodChannel是一種native與flutter之間互相發送數據的通信方式,顧名思義,通過MethodChannel就能調用native與flutter中相對應的方法,該種方式有返回值。它的使用方式如下。
首先來看Android端的代碼實現。
public class MethodChannelPlugin implements MethodChannel.MethodCallHandler { private Activity activity; private MethodChannel channel; public static MethodChannelPlugin registerWith(FlutterView flutterView) { MethodChannel channel = new MethodChannel(flutterView, "MethodChannelPlugin"); MethodChannelPlugin methodChannelPlugin = new MethodChannelPlugin((Activity) flutterView.getContext(), channel); channel.setMethodCallHandler(methodChannelPlugin); return methodChannelPlugin; } private MethodChannelPlugin(Activity activity, MethodChannel channel) { this.activity = activity; this.channel = channel; } //調用flutter端方法,無返回值 public void invokeMethod(String method, Object o) { channel.invokeMethod(method, o); } //調用flutter端方法,有返回值 public void invokeMethod(String method, Object o, MethodChannel.Result result) { channel.invokeMethod(method, o, result); } @Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) { switch (methodCall.method) { case "send"://返回的方法名 //給flutter端的返回值 result.success("MethodChannelPlugin收到:" + methodCall.arguments); Toast.makeText(activity, methodCall.arguments + "", Toast.LENGTH_SHORT).show(); if (activity instanceof FlutterAppActivity) { ((FlutterAppActivity) activity).showContent(methodCall.arguments); } break; default: result.notImplemented(); break; } } }
筆者對Android端代碼做了一個簡單的封裝,還是很好理解的。下面就來看flutter代碼實現。
class _MyHomePageState extends State<MyHomePage> { MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin"); @override void initState() { _methodChannel.setMethodCallHandler((handler) => Future<String>(() { print("_methodChannel:${handler}"); //監聽native發送的方法名及參數 switch (handler.method) { case "send": _send(handler.arguments);//handler.arguments表示native傳遞的方法參數 break; } })); super.initState(); } //native調用的flutter方法 void _send(arg) { setState(() { _content = arg; }); } String _resultContent = ""; //flutter調用native的相應方法 void _sendToNative() { Future<String> future = _methodChannel.invokeMethod("send", _controller.text); future.then((message) { setState(() { //message是native返回的數據 _resultContent = "返回值:" + message; }); }); } @override Widget build(BuildContext context) {...} }
上面就是通過MethodChannel來進行通信的代碼實現。還是比較簡單的。在Android端使用只需要調用MethodChannelPlugin的invokeMethod方法即可。在flutter端使用只需要參考_sendToNative方法的實現即可。
3.4、BasicMessageChannel
BasicMessageChannel是一種能夠在native與flutter之間互相發送消息的通信方式,它支持數據類型最多,使用范圍最廣。EventChannel與MethodChannel的應用場景可以使用BasicMessageChannel來實現,但BasicMessageChannel的應用場景就不一定能夠使用EventChannel與MethodChannel來實現。該方式有返回值。它的使用方式如下。
首先來看Android代碼的實現。
//這里支持的數據類型為String。 public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String> { private Activity activity; private BasicMessageChannel<String> messageChannel; static BasicMessageChannelPlugin registerWith(FlutterView flutterView) { return new BasicMessageChannelPlugin(flutterView); } private BasicMessageChannelPlugin(FlutterView flutterView) { this.activity = (Activity) flutterView.getContext(); this.messageChannel = new BasicMessageChannel<String>(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE); messageChannel.setMessageHandler(this); } @Override public void onMessage(String s, BasicMessageChannel.Reply<String> reply) { reply.reply("BasicMessageChannelPlugin收到:" + s); if (activity instanceof FlutterAppActivity) { ((FlutterAppActivity) activity).showContent(s); } } void send(String str, BasicMessageChannel.Reply<String> reply) { messageChannel.send(str, reply); } }
筆者對Android端代碼做了一個簡單的封裝,還是很好理解的。下面就來看flutter代碼實現。
class _MyHomePageState extends State<MyHomePage> { //StringCodec()為編碼格式 BasicMessageChannel<String> _basicMessageChannel = BasicMessageChannel("BasicMessageChannelPlugin", StringCodec()); @override void initState() { _basicMessageChannel.setMessageHandler((message) => Future<String>(() { print(message); //message為native傳遞的數據 setState(() { _content = message; }); //給Android端的返回值 return "收到Native消息:" + message; })); _controller = TextEditingController(); super.initState(); } //向native發送消息 void _sendToNative() { Future<String> future = _basicMessageChannel.send(_controller.text); future.then((message) { _resultContent = "返回值:" + message; }); } @override Widget build(BuildContext context) {...} }
上面就是通過BasicMessageChannel來進行通信的代碼實現。在Android端只需要調用BasicMessageChannelPlugin的send方法就可以向flutter發送數據,BasicMessageChannel.Reply是返回值的回調方法。在flutter端使用只需要參考_sendToNative方法的實現即可。
4、通信原理
從分析Android與Flutter通信的源碼來看,實現還是比較簡單的,都是以ByteBuffer為數據載體,然后通過BinaryMessenger來發送與接收數據。整體設計如下。
從圖中可以看出,Android側與flutter側采用了相同的設計。前面說過通信時是異步進行的,那么線程切換在哪?其實是在系統底層實現的。在Android與Flutter通信中,系統底層屏蔽了線程切換、數據拷貝等大量復雜操作。使得Android側與flutter側能方便的來進行通信。
在Android側,BinaryMessenger是一個接口,在FlutterView中實現了該接口,在BinaryMessenger的方法中通過JNI來與系統底層溝通。在Flutter側,BinaryMessenger是一個類,該類的作用就是與類window溝通,而類window才真正與系統底層溝通。
5、總結
在Android與Flutter混合開發模式下,相互之間通信的場景肯定不會少。了解Android與Flutter之間通信的各種方式及使用,有助于選用合理的方式來實現。
看完上述內容,你們掌握Android與Flutter之間怎么實現通信的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。