11 Flutter 與 Native通信原理/實(shí)戰(zhàn)筏养。
[Flutter與Android通信開發(fā)指南] (http://events.jianshu.io/p/575440e754d2)
[原始作者] (https://www.imooc.com/article/309626)
在做Flutter開發(fā)的時候逢艘,離不開Flutter之間的通信财喳,比如:初始化Flutter時Native向Dart傳遞數(shù)據(jù)删豺,Dart調(diào)用Native的相冊選擇圖片隐绵,Dart調(diào)用Native的模塊進(jìn)行一些復(fù)雜的計算,native將一些數(shù)據(jù)(GPS信息盲链、陀螺儀蝇率、傳感器等)主動傳遞給Dart等。
幾種通信場景:
- 初始化Flutter時刽沾,Native向Dart傳遞數(shù)據(jù)本慕;
- Native發(fā)送數(shù)據(jù)給Dart;
- Dart發(fā)送數(shù)據(jù)給Native侧漓;
- Dart發(fā)送數(shù)據(jù)給Native锅尘,然后Native回傳數(shù)據(jù)給Dart。
Flutter 與 Native通信機(jī)制
通信機(jī)制:通過Channel(平臺通道)來完成布蔗。消息使用Channel在Flutter與Native之間傳遞藤违。
Channel 所支持?jǐn)?shù)據(jù)類型對照表。
Dart ~ Android ~ iOS
null ~ null ~ nil(NSNull when nested)
bool ~ java.lang.Boolean ~ NSNumber numberWithBool:
int ~ java.lang.Integer ~ NSNumber numberWithInt:
int,超32bits ~ java.lang.Long ~ NSNumber numberWithLong:
double ~ java.lang.Double ~ NSNumber numberWithDouble:
String ~ java.lang.String ~ NSString
Unit8List ~ byte[] ~ FlutterStandardTypedData typedDataWithBytes:
Int32List ~ int[] ~ FlutterStandardTypedData typedDataWithInt32:
Int64List ~ long[] ~ FlutterStandardTypedData typedDataWithInt64:
Float64List~ double[] ~ FlutterStandardTypedData typedDataWithFloat64:
List ~ java.util.ArrayList ~ NSArray
Map ~ java.util.HashMap ~ NSDictionary
Flutter 定義了三種不同類型的Channel
- BasicMessageChannel:用于傳遞字符串和半結(jié)構(gòu)化的信息纵揍,持續(xù)通信顿乒,收到消息后可以回復(fù)此消息,如:Native將遍歷到的文件信息 陸續(xù) 傳遞給Dart骡男,再比如:Flutter將從服務(wù)器端 陸續(xù) 獲取到的信息交給native加工淆游,native處理完后返回等傍睹。
- MethodChannel:用于傳遞方法調(diào)用(method invocation)一次性通信:如Flutter調(diào)用native代碼,如:拍照犹菱,信息選擇等拾稳。
- EventChannel:用于數(shù)據(jù)流(event Stream)的通信,持續(xù)通信腊脱,收到消息后無法回復(fù)此消息访得,通常用于native向Dart的通信(eg:手機(jī)電量變化,網(wǎng)絡(luò)連接變化陕凹,陀螺儀悍抑,傳感器等。)
以上三種類型的Channel都是 全雙工通信杜耙,即A<=>B, Dart可以主動發(fā)送消息給platform端搜骡,并且platform接收到消息后可以做出回應(yīng);
同樣佑女,platform端可以主動發(fā)送消息給Dart端记靡,Dart端接收后返回給platform端。
BasicMessageChannel用法:
Native Android端:
1.構(gòu)造方法原型
BasicMessageChannel(BinaryMessage messenger, String name, MessageCodec<T> codec)
- BinaryMessage messenger: 消息信使团驱,是消息的發(fā)送和接收的工具摸吠,
- String name: Channel的名字,也是唯一標(biāo)識符嚎花。
- MessageCodec<T> codec: 消息的編解碼器寸痢,他有集中不同類型的實(shí)現(xiàn):
- BinaryCodec: 最為簡單的一種Codec,因?yàn)槠浞祷刂殿愋秃腿雲(yún)⒌念愋拖嗤裳。鶠槎M(jìn)制格式(Android中為ByteBuffer啼止,iOS中為NSData)。實(shí)際上丛楚,BinaryCodec在編解碼過程中什么都沒做族壳,只是原封不動將二進(jìn)制數(shù)據(jù)消息返回而已憔辫∪ば或許你會因此覺得BinaryCodec沒有意義,但是在某些情況下它非常有用贰您,比如使用BinaryCodec可以使傳遞內(nèi)存數(shù)據(jù)塊時在編解碼階段免于內(nèi)存拷貝坏平;效率高。
- StringCodec: 用于字符串與二進(jìn)制數(shù)據(jù)之間的編解碼锦亦,其編碼格式為UTF-8舶替;
- JSONMessageCodec: 用于基礎(chǔ)數(shù)據(jù)與二進(jìn)制數(shù)據(jù)之間的編解碼,其支持基礎(chǔ)數(shù)據(jù)類型以及列表杠园、字典顾瞪。其在iOS端使用了NSJSONSerialization作為序列化的工具,而在Android端則使用了其自定義的JSONUtil與StringCodec作為序列化工具;
- StandardMethodCodec: 是BasicMessageChannel的默認(rèn)編解碼器陈醒,其支持基礎(chǔ)數(shù)據(jù)類型惕橙、二進(jìn)制數(shù)據(jù)、列表钉跷、字典弥鹦,其工作原理;
2.setMessageHandler方法原型
void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler)
- BasicMessageChannel.MessageHandler<T> handler - 消息處理器爷辙,配合BinaryMessenger完成消息的處理彬坏;
在創(chuàng)建好BasicMessageChannel后,如果要讓其接收Dart發(fā)來的消息膝晾,則需要調(diào)用它的setMessageHandler方法為其設(shè)置一個消息處理器栓始。
3.BasicMessageChannel.MessageHandler原型
public interface MessageHandler<T> {
void onMessage(T var1, BasicMessageChannel.Reply<T> var2);
}
- onMessage(T var1, BasicMessageChannel.Reply<T> var2) - 用于接受消息,var1是消息內(nèi)容血当,var2是回復(fù)此消息的回調(diào)函數(shù)混滔;
4. send方法原型(向Dart發(fā)送消息)
void send(T message)
void send(T message, BasicMessageChannel.Reply<T> callback)
- T message - 要傳遞給Dart的具體信息;
- BasicMessageChannel.Reply<T> callback - 消息發(fā)出去后歹颓,收到Dart的回復(fù)的回調(diào)函數(shù)坯屿;
在創(chuàng)建好BasicMessageChannel后,如果要向Dart發(fā)送消息巍扛,可以調(diào)用它的send方法向Dart傳遞數(shù)據(jù)领跛。
public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String>, BasicMessageChannel.Reply<String> {
private final Activity activity;
private final 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<>(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
// 設(shè)置消息處理器,處理來自Dart的消息
messageChannel.setMessageHandler(this);
}
@Override
public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {//處理Dart發(fā)來的消息
reply.reply("BasicMessageChannel收到:" + s);//可以通過reply進(jìn)行回復(fù)
if (activity instanceof IShowMessage) {
((IShowMessage) activity).onShowMessage(s);
}
Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
}
/**
* 向Dart發(fā)送消息撤奸,并接受Dart的反饋
* @param message 要給Dart發(fā)送的消息內(nèi)容
* @param callback 來自Dart的反饋
*/
void send(String message, BasicMessageChannel.Reply<String> callback) {
messageChannel.send(message, callback);
}
@Override
public void reply(String s) {
}
}
Dart端:
1.構(gòu)造方法原型
const BasicMessageChannel(this.name, this.codec);
- String name: Channel名字吠昭,要和Native端保持一致。
- MessageCodec<T> codec: 消息的編解碼器胧瓜,要和native端保持一致矢棚,有4種類型的具體實(shí)現(xiàn)。
2.setMessageHandler方法原型
void setMessageHandler(Future<T> handler(T message))
- Future<T> handler(T message)- 消息處理器府喳,配合BinaryMessager完成消息的處理蒲肋;
創(chuàng)建好BasicMessageChannel后,如果要讓其接收Native發(fā)來的消息钝满,則需要調(diào)用它的setMessageHandler方法為其設(shè)置一個消息處理器兜粘。
3.send方法原型
Future<T> send(T message)
- T message: 要傳遞給native的具體消息;
- Future<T>: 消息發(fā)出去后弯蚜,收到Native回復(fù)的回調(diào)函數(shù)孔轴;
在創(chuàng)建好BasicMessageChannel后,如果要向Native發(fā)送消息碎捺,可以調(diào)用它的send方法向Native傳遞數(shù)據(jù)路鹰。
import 'package:flutter/services.dart';
const BasicMessageChannel _basicMessageChannel = BasicMessageChannel('BasicMessageChannelPlugin', StringCodec());
// 使用BasicMessageChannel接收來自Native的消息贷洲,并向Native回復(fù)。
// - message是Native發(fā)來的消息晋柱;
// - Future<String> 是Flutter向Native回復(fù)的消息恩脂。
/// 被動發(fā)送消息
_basicMessageChannel.setMessageHandler((String message) => Future<String>((){
setState((){
showMessage = message; // 收到Native發(fā)來的消息
});
return "接收Native的消息:" + message; // 回復(fù)消息給native
}));
// 主動發(fā)送消息:使用BasicMessageChannel向Native發(fā)送消息,并接收Native的回復(fù)趣斤。
String response;
try {
// Flutter主動發(fā)送消息俩块,并接收native回復(fù)。
response = await _basicMessageChannel.send(value);
} on PlatformException catch(e) {
print(e);
}
MethodChannel用法:
Native Android端:
1.構(gòu)造方法原型
//會構(gòu)造一個StandardMethodCodec.INSTANCE類型的MethodCodec
MethodChannel(BinaryMessenger messenger, String name)
MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec)
- BinaryMessenger messenger - 消息信使浓领,是消息的發(fā)送與接收的工具玉凯;
- String name - Channel的名字,也是其唯一標(biāo)識符联贩;
- MethodCodec codec - 用作MethodChannel的編解碼器漫仆;
2.setMethodCallHandler方法原型
setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler)
- @Nullable MethodChannel.MethodCallHandler handler - 消息處理器,配合BinaryMessenger完成消息的處理泪幌;
在創(chuàng)建好MethodChannel后盲厌,需要調(diào)用它的setMessageHandler方法為其設(shè)置一個消息處理器,以便能加收來自Dart的消息祸泪。
3.MethodChannel.MethodCallHandler原型
public interface MethodCallHandler {
void onMethodCall(MethodCall var1, MethodChannel.Result var2);
}
- onMethodCall(MethodCall call, MethodChannel.Result result) - 用于接受消息吗浩,
call是消息內(nèi)容,它有兩個成員變量String類型的call.method表示調(diào)用的方法名没隘,Object 類型的call.arguments表示調(diào)用方法所傳遞的入?yún)ⅲ?br> MethodChannel.Result result是回復(fù)此消息的回調(diào)函數(shù)提供了result.success懂扼,result.error,result.notImplemented方法調(diào)用右蒲;
public class MethodChannelPlugin implements MethodCallHandler {
private final Activity activity;
/**
* Plugin registration.
*/
public static void registerWith(FlutterView flutterView) {
MethodChannel channel = new MethodChannel(flutterView, "MethodChannelPlugin");
MethodChannelPlugin instance = new MethodChannelPlugin((Activity) flutterView.getContext());
channel.setMethodCallHandler(instance);
}
private MethodChannelPlugin(Activity activity) {
this.activity = activity;
}
@Override
public void onMethodCall(MethodCall call, Result result) {
switch (call.method) {//處理來自Dart端的方法調(diào)用
case "showMessage":
showMessage(call.arguments());
result.success("MethodChannelPlugin收到:" + call.arguments);//返回結(jié)果給Dart
break;
default: // 方法未實(shí)現(xiàn)
result.notImplemented();
}
}
/**
* 展示來自Dart的數(shù)據(jù)
* @param arguments
*/
private void showMessage(String arguments) {
if (activity instanceof IShowMessage) {
((IShowMessage) activity).onShowMessage(arguments);
}
Toast.makeText(activity, arguments, Toast.LENGTH_SHORT).show();
}
}
Dart端:
1.構(gòu)造方法原型
const MethodChannel(this.name, [this.codec= const StandardMethodCodec()])
- String name: channel的名字阀湿,要和Native端保持一致;
- MechodCode codec:消息的編解碼器瑰妄,要和Native端保持一致陷嘴。
2.invokeMethod方法原型
Future<T> invodeMethod<T>(String method, [dynamic arguments])
- method: 要調(diào)用Native的方法名;
- [dynamic arguments]:調(diào)用Native方法傳遞的參數(shù)间坐,可不傳灾挨。
const MethodChannel _methodChannelPlugin = MethodChannel('MethodChannelPlugin');
...
String response;
try {
// response dart調(diào)用native代碼返回的結(jié)果。調(diào)用原生send方法眶诈。
response = await _methodChannelPlugin.invokeMethod('send', value);
} on PlatformException catch (e) {
print(e);
}
EventChannel用法:
Native Android端:
1.構(gòu)造方法原型
//會構(gòu)造一個StandardMethodCodec.INSTANCE類型的MethodCodec
EventChannel(BinaryMessenger messenger, String name)
//或
EventChannel(BinaryMessenger messenger, String name, MethodCodec codec)
- BinaryMessenger messenger - 消息信使涨醋,是消息的發(fā)送與接收的工具瓜饥;
- String name - Channel的名字逝撬,也是其唯一標(biāo)識符;
- MethodCodec codec - 用作EventChannel的編解碼器乓土;
2.setStreamHandler方法原型
void setStreamHandler(EventChannel.StreamHandler handler)
- EventChannel.StreamHandler handler - 消息處理器宪潮,配合BinaryMessenger完成消息的處理溯警;
在創(chuàng)建好EventChannel后,如果要讓其接收Dart發(fā)來的消息狡相,則需要調(diào)用它的setStreamHandler方法為其設(shè)置一個消息處理器梯轻。
3.EventChannel.StreamHandler原型
public interface StreamHandler {
void onListen(Object args, EventChannel.EventSink eventSink);
void onCancel(Object o);
}
- void onListen(Object args, EventChannel.EventSink eventSink) - Flutter Native監(jiān)聽事件時調(diào)用,Object args是傳遞的參數(shù)尽棕,EventChannel.EventSink eventSink是Native回調(diào)Dart時的會回調(diào)函數(shù)喳挑,eventSink提供success、error與endOfStream三個回調(diào)方法分別對應(yīng)事件的不同狀態(tài)滔悉;
- void onCancel(Object o) - Flutter取消監(jiān)聽時調(diào)用伊诵;
public class EventChannelPlugin implements EventChannel.StreamHandler {
private List<EventChannel.EventSink> eventSinks = new ArrayList<>();
static EventChannelPlugin registerWith(FlutterView flutterView) {
EventChannelPlugin plugin = new EventChannelPlugin();
new EventChannel(flutterView, "EventChannelPlugin").setStreamHandler(plugin);
return plugin;
}
void sendEvent(Object params) {
for (EventChannel.EventSink eventSink : eventSinks) {
eventSink.success(params);
}
}
@Override
public void onListen(Object args, EventChannel.EventSink eventSink) {
eventSinks.add(eventSink);
}
@Override
public void onCancel(Object o) {
}
}
Dart端:
1.構(gòu)造方法原型
const EventChannel(this.name, [this.codec= const StandardMethodCodec()]);
- String name: channel的名字,要和Native端保持一致回官;
- MechodCode codec:消息的編解碼器曹宴,要和Native端保持一致。
2.receiveBroadcastStream 方法原型
Stream<dynamic> receiveBroadcastStream([dynamic arguments])
- dynamic arguments: 監(jiān)聽事件時向Native傳遞的數(shù)據(jù)歉提。
初始化一個廣播用于從Channel中接收數(shù)據(jù)笛坦,它返回一個Stream接下來需要調(diào)用Stream的listen方法來完成注冊。另外需要在頁面銷毀時苔巨,調(diào)用Stream的cancel方法來取消監(jiān)聽版扩。
class EventTest extends StatefulWidget {
const EventTest({Key? key}) : super(key: key);
@override
State<EventTest> createState() => _EventTestState();
}
const EventChannel _eventChannelPlugin = EventChannel('EventChannelPlugin');
class _EventTestState extends State<EventTest> {
late StreamSubscription? _streamSubscription;
String showMessage = '';
@override
void initState() {
// 注冊
_streamSubscription = _eventChannelPlugin
.receiveBroadcastStream()
.listen(_onToDart, onError: _onToDartError);
super.initState();
}
@override
void dispose() {
if (_streamSubscription != null) { // 取消注冊
_streamSubscription?.cancel();
_streamSubscription = null;
}
super.dispose();
}
void _onToDart(message) {
setState(() {
showMessage = message;
});
}
void _onToDartError(error) {
print(error);
}
@override
Widget build(BuildContext context) {
return Container();
}
}
完整通信例子
1. 初始化Flutter時Native向Dart傳遞數(shù)據(jù)
在Flutter的API中提供了Native在初始化Dart頁面時傳遞數(shù)據(jù)給Dart的方式,這種傳遞數(shù)據(jù)的方式比下文中所講的其他幾種傳遞數(shù)據(jù)的方式發(fā)生的時機(jī)都早侄泽。
因?yàn)楹苌儆匈Y料介紹這種方式资厉,所以可能有很多同學(xué)還不知道這種方式,不過不要緊蔬顾,接下來我就向大家介紹如何使用這種方式來傳遞數(shù)據(jù)給Dart宴偿。
Android向Flutter傳遞初始化數(shù)據(jù)initialRoute
Flutter允許我們在初始化Flutter頁面時向Flutter傳遞一個String類型的initialRoute參數(shù),從參數(shù)名字它是用作路由名的诀豁,但是既然Flutter給我們開了這個口子窄刘,那我們是不是可以搞點(diǎn)事情啊,傳遞點(diǎn)我們想傳的其他參數(shù)呢舷胜,比如:
FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.someContainer, Flutter.createFragment("{name:'devio',dataList:['aa','bb',''cc]}"));
tx.commit();
//or
View flutterView = Flutter.createView(
MainActivity.this,
getLifecycle(),
"route1"
);
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
layout.leftMargin = 100;
layout.topMargin = 200;
addContentView(flutterView, layout);
然后在Flutter module通過如下方式獲让浼:
import 'dart:ui';//要使用window對象必須引入
String initParams=window.defaultRouteName;
//序列化成Dart obj 敢你想干的
...
通過上述方案的講解是不是給大家分享了一個新的思路呢。
接下來烹骨,我們先來看一下如何在Android上來傳遞這些初始化數(shù)據(jù)翻伺。
實(shí)例2. Native到Dart的通信(Native發(fā)送數(shù)據(jù)給Dart)
在Flutter 中Native向Dart傳遞消息可以通過BasicMessageChannel或EventChannel都可以實(shí)現(xiàn):
通過BasicMessageChannel的方式:
通過EventChannel的方式:
以上就是使用不同Channel實(shí)現(xiàn)Native到Dart通信的原理及方式,接下來我們來看一下實(shí)現(xiàn)Dart到Native之間通信的原理及方式沮焕。
3. Dart到Native的通信(Dart發(fā)送數(shù)據(jù)給Native)
在Flutter 中Dart向Native傳遞消息可以通過BasicMessageChannel或MethodChannel都可以實(shí)現(xiàn):
通過BasicMessageChannel的方式
通過MethodChannel的方式
https://hanshuliang.blog.csdn.net/article/details/119998492
Dart端:
Android端:
String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
// url 'D:/FlutterLearn/flutter_bybird1115/flutter_module/build/host/outputs/repo'
url '../flutter_module/build/host/outputs/repo'
}
maven {
url '$storageUrl/download.flutter.io'
}
}