Flutter 混合开发(Android)Flutter跟Native相互通信

前言

Flutter 作为混合开发,跟native端做一些交互在所难免,比如说调用原生系统传感器、原生端的网络框架进行数据请求就会用到 Flutter 调用android 及android 原生调用 Flutter的方法,这里就涉及到Platform Channels(平台通道)

Platform Channels (平台通道)

Flutter 通过Channel 与客户端之间传递消息,如图:

image.png

图中就是通过MethodChannel的方式实现Flutter 与客户端之间的消息传递。MethodChannel是Platform Channels中的一种,Flutter有三种通信类型:

BasicMessageChannel:用于传递字符串和半结构化的信息

MethodChannel:用于传递方法调用(method invocation)通常用来调用native中某个方法

EventChannel: 用于数据流(event streams)的通信。有监听功能,比如电量变化之后直接推送数据给flutter端。

为了保证UI的响应,通过Platform Channels传递的消息都是异步的。

更多关于channel原理可以去看这篇文章:channel原理篇

Platform Channels 使用

1.MethodChannel的使用

原生客户端写法(以Android 为例)

首先定义一个获取手机电量方法

private int getBatteryLevel() {

return 90;

}

这函数是要给Flutter 调用的方法,此时就需要通过 MethodChannel 来建立这个通道了。

首先新增一个初始化 MethodChannel 的方法

private String METHOD_CHANNEL = "common.flutter/battery";

private String GET_BATTERY_LEVEL = "getBatteryLevel";

private MethodChannel methodChannel;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

GeneratedPluginRegistrant.registerWith(this);

initMethodChannel();

getFlutterView().postDelayed(() ->

methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() {

@Override

public void success(@Nullable Object o) {

Log.d(TAG, "get_message:" + o.toString());

}

@Override

public void error(String s, @Nullable String s1, @Nullable Object o) {

}

@Override

public void notImplemented() {

}

}), 5000);

}

private void initMethodChannel() {

methodChannel = new MethodChannel(getFlutterView(), METHOD_CHANNEL);

methodChannel.setMethodCallHandler(

(methodCall, result) -> {

if (methodCall.method.equals(GET_BATTERY_LEVEL)) {

int batteryLevel = getBatteryLevel();

if (batteryLevel != -1) {

result.success(batteryLevel);

} else {

result.error("UNAVAILABLE", "Battery level not available.", null);

}

} else {

result.notImplemented();

}

});

}

private int getBatteryLevel() {

return 90;

}

METHOD_CHANNEL 用于和flutter交互的标识,由于一般情况下会有多个channel,在app里面需要保持唯一性

MethodChannel 都是保存在以通道名为Key的Map中。所以要是设了两个名字一样的channel,只有后设置的那个会生效。

onMethodCall 有两个参数,onMethodCall 里包含要调用的方法名称和参数。Result是给Flutter的返回值。方法名是客户端与Flutter统一设定。通过if/switch语句判断 MethodCall.method 来区分不同的方法,在我们的例子里面我们只会处理名为“getBatteryLevel”的调用。在调用本地方法获取到电量以后通过 result.success(batteryLevel) 调用把电量值返回给Flutter。

MethodChannel-Flutter 端

直接先看一下Flutter端的代码

class _MyHomePageState extends State<MyHomePage> {

int _counter = 0;

static const platform = const MethodChannel('common.flutter/battery');

void _incrementCounter() {

setState(() {

_counter++;

_getBatteryLevel();

});

}

@override

Widget build(BuildContext context) {

platform.setMethodCallHandler(platformCallHandler);

return Scaffold(

appBar: AppBar(

title: Text(widget.title),

),

body: Center(

child: Column(

mainAxisAlignment: MainAxisAlignment.center,

children: <Widget>[

Text(

'You have pushed the button this many times:',

),

Text(

'$_counter',

style: Theme.of(context).textTheme.display1,

),

Text('$_batteryLevel'),

],

),

),

floatingActionButton: FloatingActionButton(

onPressed: _incrementCounter,

tooltip: 'Increment',

child: Icon(Icons.add),

),

);

}

String _batteryLevel = 'Unknown battery level.';

Future<Null> _getBatteryLevel() async {

String batteryLevel;

try {

final int result = await platform.invokeMethod('getBatteryLevel');

batteryLevel = 'Battery level at $result % .';

} on PlatformException catch (e) {

batteryLevel = "Failed to get battery level: '${e.message}'.";

}

setState(() {

_batteryLevel = batteryLevel;

});

}

//客户端调用

Future<dynamic> platformCallHandler(MethodCall call) async {

switch (call.method) {

case"get_message":

return"Hello from Flutter";

break;

}

}

}

上面代码解析:

首先,定义一个常量result.success(platform),和Android客户端定义的channel一致;

接下来定义一个 result.success(_getBatteryLevel())方法,用来调用Android 端的方法,result.success(final int result = await platform.invokeMethod('getBatteryLevel');) 这行代码就是通过通道来调用Native(Android)方法了。因为MethodChannel是异步调用的,所以这里必须要使用await关键字。

在上面Android代码中我们把获取到的电量通过result.success(batteryLevel);返回给Flutter。这里await表达式执行完成以后电量就直接赋值给result变量了。然后通过result.success(setState); 去改变Text显示值。到这里为止,是通过Flutter端调用原生客户端方法。

MethodChannel 其实是一个可以双向调用的方法,在上面的代码中,其实我们也体现了,通过原生客户端调用Flutter的方法。

在原生端通过 methodChannel.invokeMethod 的方法调用

methodChannel.invokeMethod("get_message", null, new MethodChannel.Result() {

@Override

public void success(@Nullable Object o) {

Log.d(TAG, "get_message:" + o.toString());

}

@Override

public void error(String s, @Nullable String s1, @Nullable Object o) {

}

@Override

public void notImplemented() {

}

});

在Flutter端就需要给MethodChannel设置一个MethodCallHandler

static const platform = const MethodChannel('common.flutter/battery');

platform.setMethodCallHandler(platformCallHandler);

Future<dynamic> platformCallHandler(MethodCall call) async {

switch (call.method) {

case"get_message":

return"Hello from Flutter";

break;

}

}

以上就是MethodChannel的相关用法了。

EventChannel

将数据推送给Flutter端,类似我们常用的推送功能,有需要就推送给Flutter端,是否需要去处理这个推送由Flutter那边决定。相对于MethodChannel是主动获取,EventChannel则是被动推送。

EventChannel 原生客户端写法

private String EVENT_CHANNEL = "common.flutter/message";

private int count = 0;

private Timer timer;

private void initEventChannel() {

new EventChannel(getFlutterView(), EVENT_CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {

@Override

public void onListen(Object arguments, EventChannel.EventSink events) {

timer.schedule(new TimerTask() {

@Override

public void run() {

if (count < 10) {

count++;

events.success("当前时间:" + System.currentTimeMillis());

} else {

timer.cancel();

}

}

}, 1000, 1000);

}

@Override

public void onCancel(Object o) {

}

});

}

在上面的代码中,我们做了一个定时器,每秒向Flutter推送一个消息,告诉Flutter我们当前时间。为了防止一直倒计时,我这边做了个计数,超过10次就停止发送。

EventChannel Flutter端

String message = "not message";

static const eventChannel = const EventChannel('common.flutter/message');

@override

void initState() {

super.initState();

eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);

}

void _onEvent(Object event) {

setState(() {

message =

"message: $event";

});

}

void _onError(Object error) {

setState(() {

message = 'message: unknown.';

});

}

上面的代码就是Flutter端接收原生客户端数据,通过_onEvent 来接收数据,将数据显示Text。这个实现相对简单,如果要达到业务分类,需要将数据封装成json,通过json数据包装一些对应业务标识和数据来做区分。

BasicMessageChannel

BasicMessageChannel (主要是传递字符串和一些半结构体的数据)

BasicMessageChannel Android端

private void initBasicMessageChannel() {

BasicMessageChannel<Object> basicMessageChannel = new BasicMessageChannel<>(getFlutterView(), BASIC_CHANNEL, StandardMessageCodec.INSTANCE);

//主动发送消息到flutter 并接收flutter消息回复

basicMessageChannel.send("send basic message", (object)-> {

Log.e(TAG, "receive reply msg from flutter:" + object.toString());

});

//接收flutter消息 并发送回复

basicMessageChannel.setMessageHandler((object, reply)-> {

Log.e(TAG, "receive msg from flutter:" + object.toString());

reply.reply("reply:got your message");

});

}

BasicMessageChannel Flutter端

  static const basicChannel = const BasicMessageChannel('common.flutter/basic', StandardMessageCodec());

//发送消息到原生客户端 并且接收到原生客户端的回复

Future<String> sendMessage() async {

String reply = await basicChannel.send('this is flutter');

print("receive reply msg from native:$reply");

return reply;

}

//接收原生消息 并发送回复

void receiveMessage() async {

basicChannel.setMessageHandler((msg) async {

print("receive from Android:$msg");

return"get native message";

});

上面例子中用到的编解码器为StandardMessageCodec ,例子中通信都是String,用StringCodec也可以。

以上就是Flutter提供三种platform和dart端的消息通信方式。



以上是 Flutter 混合开发(Android)Flutter跟Native相互通信 的全部内容, 来源链接: utcz.com/a/26603.html

回到顶部