欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net

Flutter MethodChannel 在 OpenHarmony 上调用原生能力的实践

前言

最近在做 Flutter for OpenHarmony 项目时,开始慢慢遇到一些 Flutter 本身不好直接处理的功能。

比如:

  • 获取设备信息
  • 调用系统相册
  • 读取电池状态
  • 获取系统版本
  • 调用鸿蒙原生能力

这些功能很多都需要和 OpenHarmony 原生层通信。

Flutter 虽然跨平台能力很强,但真正做业务项目的时候,原生通信其实绕不过去。

这次项目里我主要用的是:


MethodChannel

整体接下来之后,感觉和 Android 原生通信思路比较像,但 OpenHarmony 下还是有一些细节需要注意。

这篇文章主要记录一下我这次在鸿蒙项目里的 MethodChannel 实践过程,以及踩过的一些坑。


一、为什么需要 MethodChannel

Flutter 本身提供了很多组件。

但有些能力:

只能通过原生层获取。

例如:

功能 Flutter 是否直接支持
系统版本 部分支持
电池信息 不完整
相册权限 原生更灵活
设备唯一标识 需要原生
鸿蒙系统能力 必须原生

所以最终还是需要:

Flutter ↔ OpenHarmony

进行通信。


二、MethodChannel 基础概念

Flutter 和原生通信:

核心其实就两部分:

角色 作用
Flutter 发起调用
原生层 接收并返回结果

通信方式:


MethodChannel

三、Flutter 端代码

这里我先拿“获取系统版本”举例。


创建 Channel


static const platform =
    MethodChannel(
      "com.demo.device/info",
    );

调用原生方法


Future<void> getSystemVersion() async {

  try {

    final String version =
        await platform.invokeMethod(
          "getSystemVersion",
        );

    print(version);

  } catch (e) {

    print(e);
  }
}

这里:


invokeMethod()

会主动调用原生层。


四、OpenHarmony 原生侧处理

鸿蒙这边我使用 ArkTS。


注册 MethodChannel


this.methodChannel =
  new MethodChannel(
    'com.demo.device/info'
  )

监听 Flutter 调用


this.methodChannel
  .setMethodCallHandler(
    (call, result) => {

      if (
        call.method ===
        "getSystemVersion"
      ) {

        result.success("OpenHarmony 5.0");
      }
    }
  )

Flutter 调用后:

原生层返回:


OpenHarmony 5.0

五、我最开始踩到的坑

这个问题我一开始排查了挺久。


问题现象

Flutter 调用:


invokeMethod()

直接报错:


MissingPluginException

原因

大部分情况下:

其实就是:

  • channel 名不一致
  • method 名写错
  • 原生侧没注册成功

例如:

Flutter:


com.demo.device/info

原生:


com.demo.device/test

这种很容易忽略。


后来的习惯

我现在会统一抽常量。


class ChannelName {

  static const device =
      "com.demo.device/info";
}

避免字符串乱写。


六、参数传递实践

MethodChannel 不只是返回字符串。

还可以传:

  • Map
  • List
  • int
  • bool

Flutter 端


final result =
    await platform.invokeMethod(
      "login",
      {
        "token": "123456",
        "uid": 1001,
      },
    );

原生接收


let token = call.arguments["token"];

这个用起来其实挺方便。


七、异步操作问题

这里是我后面踩得比较明显的一个问题。


问题

原生层如果:

  • 请求网络
  • 读取文件
  • 调用系统能力

可能是异步。

如果处理不好:

Flutter 会一直等待。


后来的处理方式

原生异步完成后:

主动:


result.success(data)

失败:


result.error(code, msg)

Flutter 侧:


try {

} on PlatformException catch (e) {

}

这样错误处理会清晰很多。


八、页面频繁调用的问题

这个我在设备信息页踩过。


问题现象

页面 build 时:

频繁:


invokeMethod()

导致:

  • 页面卡顿
  • 原生调用频繁
  • 通信开销增加

后来的优化

我改成:

  • 页面初始化调用一次
  • 数据缓存到 Provider
  • 避免 build 里直接调原生

例如:


initState() {
  loadDeviceInfo();
}

稳定很多。


九、OpenHarmony 生命周期问题

这个问题在鸿蒙设备上还挺容易碰到。


场景

页面切后台后:

原生能力状态变化。

Flutter 页面恢复:

数据还是旧的。


后来的方案

监听页面生命周期:


WidgetsBindingObserver

恢复前台时:

重新同步原生数据。


AppLifecycleState.resumed

这个在:

  • 电池状态
  • 网络状态
  • 系统信息

场景里挺重要。


十、不要把所有原生逻辑都塞进 Channel

这个是我后面最大的一个感受。


一开始的错误结构


if (method == "xxx") {}

if (method == "xxx2") {}

if (method == "xxx3") {}

后面 method 越来越多。

代码会非常乱。


后来的结构

我把:

  • DeviceChannel
  • FileChannel
  • BatteryChannel

拆开。

每个模块单独管理。

维护轻松很多。


十一、实际效果

优化前:

问题 表现
Channel 管理 混乱
原生调用 高频
页面卡顿 存在
错误排查 困难

优化后:

项目 效果
原生通信 更稳定
模块结构 更清晰
页面性能 更流畅
错误定位 更方便

目前这套方案已经在 OpenHarmony 项目里稳定使用了一段时间。


十二、总结

这次做 Flutter MethodChannel 在 OpenHarmony 下的实践,一个比较明显的感受是:

Flutter 真正做业务项目时:

原生通信其实很难避免。

尤其 OpenHarmony 还有很多系统能力:

目前必须通过原生层调用。

我现在项目里的原则基本是:

  • Channel 模块化
  • 名称统一管理
  • 不在 build 中频繁调用
  • 生命周期变化时同步状态
  • 原生异步结果统一处理

整体稳定性会好很多。

后面我还准备继续研究:

  • EventChannel 实时通信
  • Flutter Plugin 开发
  • OpenHarmony 原生桥接
  • Flutter 混合开发方案

希望这篇文章能给正在做 Flutter for OpenHarmony 开发的同学一点参考。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐