Flutter OHOS 如何使用 Flutter Channel

5 阅读3分钟

本文介绍如何在 Flutter OH 应用中使用三种 Platform Channel(MethodChannel、BasicMessageChannel、EventChannel)实现 Flutter 与 OpenHarmony 原生侧的双向通信。

Platform Channel 是 Flutter 与原生平台之间的标准通信机制:Dart 侧通过 Channel 发送消息或方法调用,原生侧注册 handler 接收并处理,从而实现双向数据交互。OHOS 平台的 Channel API 由 @ohos/flutter_ohos 提供,用法与 Android/iOS 一致。

三种 Channel 对比

Channel 类型通信模式适用场景示例
MethodChannel请求-响应一次性方法调用,Dart 调用原生方法并获取返回值获取电池电量
BasicMessageChannel双向消息简单的双向消息传递,Dart 发送消息原生回复发送数字,原生按奇偶返回不同字符串
EventChannel持续事件流原生持续向 Dart 推送数据原生通过 EventSink 推送带时间戳的事件

数据流

三种 Channel 的调用方向如下:

MethodChannel(请求-响应)

sequenceDiagram
    participant Dart
    participant Native
    Dart->>Native: invokeMethod("getBatteryLevel")
    Native-->>Dart: result.success(level)

BasicMessageChannel(双向消息)

sequenceDiagram
    participant Dart
    participant Native
    Dart->>Native: send(message)
    Native-->>Dart: reply.reply(result)

EventChannel(持续事件流)

sequenceDiagram
    participant Dart
    participant Native
    Native->>Dart: eventSink.success(event)
    Note over Dart: listen() 回调接收事件
    Dart->>Native: invokeMethod("callEvent")
    Native->>Dart: eventSink.success(event)

前置条件

  • 已完成 Flutter OH 开发环境搭建。
  • 已创建 Flutter OH 应用,本示例命名为 channel_demo。
  • 已使用 DevEco Studio 打开 channel_demo/ohos 工程。

实现步骤

第一步:实现原生侧插件(ArkTS)

原生侧代码位于 ohos/entry/src/main/ets/entryability/ 目录下。新建 BatteryPlugin.ets,实现 FlutterPlugin 接口,在 onAttachedToEngine 中创建三种 Channel 并注册 handler:

import {
  Any,
  BasicMessageChannel,
  EventChannel,
  FlutterPlugin,
  Log,
  MethodCall,
  MethodChannel,
  StandardMessageCodec
} from '@ohos/flutter_ohos';
import { FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
import { batteryInfo } from '@kit.BasicServicesKit';
import { MethodResult } from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import { EventSink } from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel';
import { Reply } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BasicMessageChannel';

const TAG = "BatteryPluginTag";

export default class BatteryPlugin implements FlutterPlugin {
  private channel?: MethodChannel;
  private basicChannel?: BasicMessageChannel<Any>;
  private eventChannel?: EventChannel;
  private eventSink?: EventSink;
  private api = new BatteryApi();

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    // --- MethodChannel ---
    // 接收 Dart 侧的方法调用,根据方法名分发处理
    this.channel = new MethodChannel(binding.getBinaryMessenger(), "samples.flutter.dev/battery");
    let that = this;
    this.channel.setMethodCallHandler({
      onMethodCall(call: MethodCall, result: MethodResult) {
        switch (call.method) {
          case "getBatteryLevel":
            that.api.getBatteryLevel(result);
            break;
          case "callEvent":
            that.eventSink?.success("Success at " + new Date());
            break;
          default:
            result.notImplemented();
            break;
        }
      }
    })

    // --- BasicMessageChannel ---
    // 接收 Dart 侧的消息,处理后通过 reply 回复
    this.basicChannel = new BasicMessageChannel(binding.getBinaryMessenger(), "samples.flutter.dev/basic_channel", new StandardMessageCodec());
    this.basicChannel.setMessageHandler({
      onMessage(message: Any, reply: Reply<Any>) {
        Log.i(TAG, "message=" + message);
        if (message % 2 == 0) {
          reply.reply("run with if case.");
        } else {
          reply.reply("run with else case");
        }
      }
    })

    // --- EventChannel ---
    // 管理 EventSink 生命周期,onListen 时保存引用用于后续推送事件
    this.eventChannel = new EventChannel(binding.getBinaryMessenger(), "samples.flutter.dev/event_channel");
    this.eventChannel.setStreamHandler({
      onListen(args: Any, events: EventSink): void {
        that.eventSink = events;
        Log.i(TAG, "onListen: " + args);
      },
      onCancel(args: Any): void {
        that.eventSink = undefined;
        Log.i(TAG, "onCancel: " + args);
      }
    });
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {
    Log.i(TAG, "onDetachedFromEngine");
    this.channel?.setMethodCallHandler(null);
    this.eventChannel?.setStreamHandler(null);
  }

  getUniqueClassName(): string {
    return "BatteryPlugin";
  }
}

class BatteryApi {
  getBatteryLevel(result: MethodResult) {
    let level: number = batteryInfo.batterySOC;
    Log.i(TAG, "level=" + level);
    if (level >= 0) {
      result.success(level);
    } else {
      Log.i(TAG, "getBatteryLevel else");
      result.error("UNAVAILABLE", "Battery level not available.", null)
    }
    Log.i(TAG, "getBatteryLevel finish");
  }
}

各组成部分的作用如下:

组成部分作用
MethodChannel接收 Dart 的 invokeMethod 调用,通过 switch 分发到具体方法。result.success() 返回成功结果,result.error() 返回错误
BasicMessageChannel接收 Dart 的 send() 消息,通过 reply.reply() 回复。需指定 MessageCodec(如 StandardMessageCodec)编解码消息
EventChannel通过 setStreamHandler 管理 EventSink。onListen 时保存引用,后续可随时调用 eventSink.success() 向 Dart 推送事件;onCancel 时清除引用
onDetachedFromEngine插件卸载时清理 handler,设为 null 避免内存泄漏
BatteryApi封装电池电量获取逻辑,使用 @kit.BasicServicesKit 的 batteryInfo.batterySOC 读取设备电量

第二步:在 EntryAbility 中注册插件

在 EntryAbility 的 configureFlutterEngine 中调用 addPlugin 注册 BatteryPlugin:

import { FlutterAbility } from '@ohos/flutter_ohos'
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import FlutterEngine from '@ohos/flutter_ohos/src/main/ets/embedding/engine/FlutterEngine';
import BatteryPlugin from './BatteryPlugin';

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    this.addPlugin(new BatteryPlugin());
  }
}

第三步:实现 Dart 侧通信

修改 lib/main.dart,创建三种 Channel 并通过按钮触发通信:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Channel Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Scaffold(
        body: PluginPage(),
      ),
    );
  }
}

class PluginPage extends StatefulWidget {
  const PluginPage({super.key});

  @override
  State<PluginPage> createState() => _PluginPageState();
}

class _PluginPageState extends State<PluginPage> {
  String message = "";
  final _platform = const MethodChannel('samples.flutter.dev/battery');
  final _eventChannel = const EventChannel('samples.flutter.dev/event_channel');
  final _basicChannel = const BasicMessageChannel(
      "samples.flutter.dev/basic_channel", StandardMessageCodec());
  int count = 0;

  @override
  void initState() {
    super.initState();
    _initListener();
  }

  void _initListener() {
    // 监听 EventChannel 事件流
    _eventChannel.receiveBroadcastStream().listen((event) {
      setState(() {
        message = "EventChannel event=$event";
      });
    });
  }

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      // MethodChannel:调用原生方法并获取返回值
      final result = await _platform.invokeMethod<int>('getBatteryLevel');
      batteryLevel = 'Battery level at $result % .';
    } on PlatformException catch (e) {
      batteryLevel = "Failed to get battery level: '${e.message}'.";
    }

    setState(() {
      message = batteryLevel;
    });
  }

  Future<void> _testStreamCall() async {
    try {
      // 通过 MethodChannel 触发原生端调用 EventSink 推送事件
      await _platform.invokeMethod('callEvent');
    } on PlatformException catch (e) {
      print(e);
    }
  }

  Future<void> _testBasicChannel() async {
    String result;
    try {
      // BasicMessageChannel:发送消息并接收原生端回复
      result = await _basicChannel.send(++count) as String;
    } on PlatformException catch (e) {
      result = "Error: $e";
    }
    setState(() {
      message = result;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(message),
          ElevatedButton(
            onPressed: () => _getBatteryLevel(),
            child: const Text("getBatteryLevel"),
          ),
          ElevatedButton(
            onPressed: () => _testStreamCall(),
            child: const Text("_testStreamCall"),
          ),
          ElevatedButton(
            onPressed: () => _testBasicChannel(),
            child: const Text("_testBasicChannel"),
          ),
        ],
      ),
    );
  }
}

三种 Channel 的 Dart 侧用法如下:

Channel创建方式调用方式
MethodChannelconst MethodChannel('name')await channel.invokeMethod<T>('methodName')
BasicMessageChannelconst BasicMessageChannel('name', codec)await channel.send(message)
EventChannelconst EventChannel('name')channel.receiveBroadcastStream().listen((event) {...})

Demo 参考

完整 demo 请参考 channel_demo