在Flutter鸿蒙开发中,平台通道的选型直接影响性能和代码可维护性。MethodChannel适合一次调用一次返回,但面对传感器、电量变化这类高频推送数据,必须用EventChannel。本文基于鸿蒙真机(nova12u, HarmonyOS 6.0)的实战Demo,拆解EventChannel的定义、Stream管理、生命周期清理、模拟数据兜底以及常见坑点,并给出ArkTS原生端注册的参考代码。
三种通道,各司其职
Flutter的平台通道共三种:MethodChannel(Flutter主动调用,原生响应,Future包装)、EventChannel(原生主动推数据,Flutter用Stream接收)、BasicMessageChannel(全双工自定义消息,一般少用)。三者共享同一个BinaryMessenger,通道名不能重复,否则冲突。
EventChannel的核心机制
原生端维护一个事件源(传感器、定时器、系统广播),通过EventSink持续向Flutter推数据。Flutter端调用receiveBroadcastStream().listen()后,连接一直保持,直到Flutter端cancel或原生主动关闭。底层使用StandardMethodCodec编解码,支持int、double、String、List、Map等标准类型。
Flutter端关键代码
定义通道(static const,仅作标识):- static const _eventChannel = EventChannel('com.example/accelerometer');
- static const _batteryChannel = EventChannel('com.example/battery');
复制代码 开始监听:调用receiveBroadcastStream().listen(),传入三个回调:数据回调、错误回调、完成回调。listen()返回StreamSubscription,这是管理订阅生命周期的关键。- _eventSub = _eventChannel.receiveBroadcastStream().listen(
- (data) { ... }, // 数据更新
- onError: (error) { ... },
- onDone: () { ... },
- );
复制代码 加速度传感器数据是X、Y、Z三个double值,用Row展示卡片。电池状态用图标和颜色区分电量区间(>60绿,20-60橙,<20红)。
StreamSubscription生命周期管理
必须在dispose中取消订阅,否则页面切走后回调仍执行已销毁的State,导致异常或内存泄漏。做法:- @override
- void dispose() {
- _stopListening();
- super.dispose();
- }
- void _stopListening() {
- _eventSub?.cancel();
- _batterySub?.cancel();
- setState(() => _status = '已停止');
- }
复制代码 使用?.安全调用,防止未初始化。此外StreamSubscription还提供pause()和resume(),可配合WidgetsBindingObserver在应用切后台时暂停UI刷新。
模拟数据兜底
当原生端未注册EventChannel时,Flutter端listen会收到MissingPluginException。Demo捕获onError后自动切换到本地模拟数据,保证UI可展示、功能可测试:- _simulateBattery() {
- var level = 85;
- Timer.periodic(Duration(seconds: 3), (timer) {
- if (!mounted) { timer.cancel(); return; }
- level = (level - 5).clamp(0, 100);
- setState(() => _batteryLevel = level);
- if (level <= 0) timer.cancel();
- });
- }
复制代码 等原生代码完成后,真实数据自动覆盖模拟数据,无需改Flutter端代码。
原生端(ArkTS)注册事件源
EventChannel要求原生端提前注册setStreamHandler,否则Flutter端listen会报错。参考代码:- import { EventChannel } from '@ohos/flutter_ohos';
- const eventChannel = new EventChannel(
- flutterEngine.getDartExecutor().getBinaryMessenger(),
- 'com.example/accelerometer'
- );
- eventChannel.setStreamHandler({
- onListen: (arguments, eventSink) => {
- sensor.on('acceleration', (data) => {
- eventSink.success([data.x, data.y, data.z]);
- });
- },
- onCancel: (arguments) => {
- sensor.off('acceleration');
- },
- });
复制代码 关键:onListen在Flutter端调listen时触发,onCancel在Flutter端cancel时触发。eventSink.success()发数据,eventSink.error()发错误。
鸿蒙ArkTS的NativeEventEmitter与EventChannel的区别
ArkTS原生有emitter.on/emit机制,但那是应用内事件总线。Flutter的EventChannel走引擎BinaryMessenger,是跨语言/跨运行时通道,不是全局总线。底层可能封装emitter,但开发者无需直接使用。
receiveBroadcastStream的细节
- 广播模式:同一EventChannel可被多次listen,每次都能收到完整原生事件流。
- 可选arguments参数:可传给原生端的onListen,用于配置采样率等(例:channel.receiveBroadcastStream({'interval': 100}))。
选型对比
- 一次性获取数据:用MethodChannel。
- 高频推送(传感器、定位、电量):用EventChannel。
- 需要Flutter控制原生状态(开关传感器):MethodChannel + EventChannel配合使用。
- 资源管理:MethodChannel调用完即结束;EventChannel需在dispose cancel。
踩过的坑
1. 原生端必须先setStreamHandler,否则listen直接报MissingPluginException(而MethodChannel可以不注册,捕获即可)。
2. dispose必须cancel所有订阅(容易遗漏多个Subscription)。
3. 鸿蒙上无原生实现时,用本地模拟数据兜底。
4. Stream默认cancelOnError=false,错误后订阅仍继续;需显式传true才自动取消,但一般不建议(错误可能可恢复)。
5. 多次调receiveBroadcastStream不会创建多个事件源,同一通道复用同一原生源。
6. EventChannel是单向(原生->Flutter),Flutter端反向控制需另开MethodChannel。
7. 不同@ohos/flutter_ohos版本API可能有差异,写前先确认官方示例。
EventChannel最适合原生端主动推数据的场景:传感器、定位、电量、网络状态、文件下载进度等。它与MethodChannel、BasicMessageChannel配合,可覆盖所有Flutter-原生通信需求。搞清本质区别后选型就不会出错。
(验证环境:Flutter · HarmonyOS 6.0 · nova12u真机演示) |