本篇开始之前,废话不多说,先上效果如下所示:
首先,本篇开始讲解EventChannel的使用,
三.EventChannel的使用
上篇中,我们提到通过MethodChannel的实现可以使flutter端可以随时随地的调用到原生端的如设备版本号,电量等系统信息,并及时返回给flutter端;但是诸如原生端声音音量的改变,电池电量的减少(如电池电量少于20%)时要主动通知到flutter端,MethodChannel并没有这样的能力,所以此时就需要引入EventChannel来解决此问题。
1.EventChannel基本介绍
通过前文EventChannel和MethodChannel的实现流程的异同点分析,我们知道了EventChannel的实现主要分为以下2个步骤,即为如何设置flutter的监听以及原生调用flutter的流程:
1.1.flutter监听流程
首先我们可以在flutter的example/lib/more_Params_page.dart 类或者插件类 lib/flutter_plugin_demo2.dart 类中定义事件响应通道_eventChannel和要监听的事件流对象_streamSubscription,如下所示:
static const EventChannel? _eventChannel = EventChannel("flutter_show_alert/event");
StreamSubscription? _streamSubscription;
在当前类的initState初始化方法中加入需要监听的代码为:
_streamSubscription = _eventChannel?.receiveBroadcastStream().listen((event) {
final Map map = event;
String? key = map["key"];
int? value = map["value"];
print("handle on ---$key---$value");
if(map["key"] == "changeVoice"){
}
else if(map["key"] == "getCount") {
setState(() {
productCount = value;
});
}
},onError: errorEventListen,onDone:doneEventListen,) as StreamSubscription;
分别实现对应的 errorEventListen 和 doneEventListen 方法后,如此在flutter端监听的内容设置完毕。
注意:为了更多减少流程,这里是以比较直接的在flutter代码的example/lib/more_Params_page.dart中加入监听原生端代码为例的;
当然,实际开发中可能监听的内容不止一个,建议在插件类 lib/flutter_plugin_demo2.dart类中进行监听,然后根据不同的key分发给flutter代码层,这样会比较好理解一些。
1.2.原生(以安卓为例)调用流程
当flutter端监听的准备工作完成后,需要在安卓原生类FlutterPluginDemo2Plugin.java中加入需要调用的代码,同样主要分为3个步骤。
1.事件派发对象和派发流的定义
定义事件派发对象和派发流代码如下,在派发流的onListen方法中把系统获取事件派发对象赋值给我们定义的事件派发对象,当系统取消事件派发流时,则把事件派发对象置空即可;
// 事件派发对象
private EventChannel.EventSink eventSink = null;
// 事件派发流
private EventChannel.StreamHandler streamHandler = new EventChannel.StreamHandler(){
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
eventSink = events;
}
@Override
public void onCancel(Object arguments) {
eventSink = null;
}
};
2.派发流的初始化和注册
// 初始化事件
EventChannel eventChannel = new EventChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_show_alert/event");
eventChannel.setStreamHandler(streamHandler);
在onAttachedToEngine 方法中对EventChannel对象进行初始化,保证这里的"flutter_show_alert/event"
要和flutter中的保持一致,并把我们定义的事件派发流设置给EventChannel去进行管理。
3.事件派发流回传flutter
在需要的地方执行
eventSink.success(map);
即可把要传递的对象回传给flutter。
当然,需要注意的是,在执行eventSink之前需要判空,避免eventSink对象被取消时时,导致崩溃。
2.借助EventChannel实现音量实时监听功能
如开篇所示gif效果图,这里以flutter中设置监听,在原生的iOS端点击音量键后把音量值实时传递给flutter,并在flutter中进行显示的功能
2.1.Flutter端
在example/lib/more_Params_page.dart类中,设置监听的内容(这里只关心changeVoice的key)
// 方法2:在需要调用的地方设置监听(各自派发)
_streamSubscription = _eventChannel?.receiveBroadcastStream().listen((event) {
final Map map = event;
String? key = map["key"];
// int? value = map["value"];
print("handle on ---$key---${map["value"]}");
if(map["key"] == "changeVoice"){
setState(() {
nowVoice = map["value"];
});
}
else if(map["key"] == "getCount") {
setState(() {
productCount = map["value"];
});
}
},onError: errorEventListen,onDone:doneEventListen,) as StreamSubscription;
2.2.Native(iOS)端
在 FlutterPluginDemo2Plugin.m类中编写代码如下所示:
1.在registerWithRegistrar方法中同MethodChannel对象,初始化注册EventChannel对象如下
FlutterEventChannel *eventChannel = [FlutterEventChannel eventChannelWithName:@"flutter_show_alert/event" binaryMessenger:[registrar messenger]];
[eventChannel setStreamHandler:instance];
注意:这里的instance是当前类的对象,为了后续可能有其他地方用到当前类,所以这里用单例实现获取该对象
2.遵循FlutterStreamHandler类协议
实现其onListenWithArguments和onCancelWithArguments方法(具体实现同安卓)
3.音量按键通知的监听
在onListenWithArguments方法中设置iOS的音量通知代码如下所示
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(systemVolumChanged:)name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];
4.监听音量变化时把音量值回传给flutter
在音量键通知回调方法中执行
self.eventSink(dic);
把相关信息包装在dic对象中,回传给flutter进行实时显示音量值大小
注意:在测试修改原生音量的情况下,iOS的设备记得用真机来调测,因为有段时间没搞了,竟然在这个问题上费了很长时间。
可以看到安卓和iOS相比而言,基本大同小异,整体来说安卓实现3个步骤,iOS是4个步骤,主要问题在于安卓的派发流的定义和FlutterStreamHandler协议实现在同一步骤之中,而iOS则分了2步,当然主要是因为flutter底层中对iOS这块接口的定义是采用协议的形式,如果采用block的方式,也是可以实现同安卓的3步。
好了,通过本篇对EventChannel的讲解说明,可以发现EventChannel主要目的是实现原生可随时通知到flutter,而上篇的MethodChannel对象则实现的是flutter调用安卓的功能,如此二者形成了一个闭环,可以互相调用对方的方法,如此很自然的,就会想到原生擅长什么,flutter擅长什么,二者是不是可以融合在一起,很自然的混合开发就出现了。
不过别着急,在我们研究混合开发之前我们先解决另一个小问题,发现没,现在做到的是flutter和native之间数据的打通互传,但是当把原生view界面如何在flutter中显示,比如常见的地图,直播类的原生界面如何在flutter上完美展现,这个问题,我们下次再来研究。
flutter插件基础之调用MethodChannel的基本使用(二)
本篇,完~~