Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 处理 airplane_mode_checker

飞行模式是个「用户开了就想让应用知道」的状态:离线阅读要暂停同步、下载要暂停、定位要降级。安卓用 BroadcastReceiver(ACTION_AIRPLANE_MODE_CHANGED) 监听,iOS 用 NSNotification。airplane_mode_checker 把这两套收成「一个查询方法 + 一条事件流」,本批里它和 system_settings_2 都属于「系统设置」这条能力线——只不过一个是读状态,一个是打开设置页。

本文以 airplane_mode_checker 3.3.0 为例,记录我如何保留原有 Dart API、在鸿蒙端用 settings.getValueSync + 系统公共事件接入真实状态读取与变化监听,并在 HarmonyOS API 26 真机上完成构建、安装、启动与调用验证。

适配仓库: oh-flutter/airplane_mode_checker

适配分支: feat/ohos_airplane_mode_checker_3.3.0

适配 TAG: 3.3.0-ohos-1.0.0-beta.1

一、先看最终效果

在这里插入图片描述

图 1:HarmonyOS API 26 真机启动自检台。页面显示「平台版本:未查询 / 单次查询:未查询 / 事件通道:未订阅」,下方三个按钮对应查版本、查状态、订阅事件。

图 2:依次点击三个按钮后的完整结果——平台版本 OpenHarmony OpenHarmony-7.0.0.105、单次查询 ON(飞行模式已开)、事件通道 订阅中(已收 1 次)· 最近事件:ON。

验证点实测结果证据
Flutter OH 3.47 工程构建通过entry-default-signed.hap

| 安装、启动(真机) | 通过 | 图 1 |
| getPlatformVersion() | OpenHarmony OpenHarmony-7.0.0.105 | 图 2 |
| checkAirplaneMode() | on(读到系统真实状态) | 图 2 |
| 事件通道订阅 | 订阅后立即收到 1 次当前值 on | 图 2 |

二、这个库原来是怎么工作的

airplane_mode_checker 的接口面很小,但同时用了两条通道:

Dart AirplaneModeChecker
  -> MethodChannel('airplane_mode_checker')
       getPlatformVersion() -> String
       checkAirplaneMode({defaultValue}) -> "ON" / "OFF"
  -> EventChannel('airplane_mode_checker_stream')
       先推一次当前值,之后每次开关变化推一次

安卓侧 checkAirplaneMode 读 Settings.Global.AIRPLANE_MODE_ON,监听走 BroadcastReceiver;iOS 侧读 AVAudioSession 之外的系统状态并监听 NSNotification。

注意 checkAirplaneMode 有个 defaultValue 参数——上游的意思是「读不到就返回调用方给的默认值」,这个语义在鸿蒙侧必须保留。

三、实际环境

Flutter 鸿蒙环境搭建不在本文重复展开,直接参考 Flutter OH 环境搭建指南。

组件版本
Flutter OH3.47.4-ohos-1.0.4
Dart3.13.3
DevEco Studio26.0.0 Release
HarmonyOS SDKAPI 26
测试设备华为真机 6UMBB26319007180,HarmonyOS 7.0.0.105(API 26)
目标库airplane_mode_checker 3.3.0
flutter --version
hdc list targets

四、确认上游版本并同步到 AtomGit

上游包地址是 https://github.com/14h4i/airplane_mode_checker,版本 3.3.0,使用 MIT License。适配前我在 AtomGit 目标组织中搜索了同名库,没有找到可以直接使用的 OHOS 实现。

git clone https://atomgit.com/oh-flutter/airplane_mode_checker.git
cd airplane_mode_checker
git switch feat/ohos_airplane_mode_checker_3.3.0
git log --oneline -5

五、创建适配分支并检查工程结构

git switch -c feat/ohos_airplane_mode_checker_3.3.0

关键目录如下:

airplane_mode_checker/
├── lib/
│   └── airplane_mode_checker_method_channel.dart   # 上游通道定义,一行未改
├── ohos/
│   ├── index.ets                                   # OHOS 插件导出入口
│   └── src/main/ets/components/plugin/
│       └── AirplaneModeCheckerPlugin.ets           # 通道实现
├── example/lib/main.dart                           # 真机验证自检台
├── example/ohos/                                   # Flutter OH 宿主工程
├── pubspec.yaml                                    # 声明 ohos 平台
└── README.OpenHarmony_CN.md                        # 鸿蒙适配说明

pubspec.yaml 中的关键声明是:

flutter:
  plugin:
    platforms:
      ohos:
        pluginClass: AirplaneModeCheckerPlugin

六、适配过程:两条通道 + 一个坑

6.1 同时注册方法通道和事件通道

const METHOD_CHANNEL: string = 'airplane_mode_checker';
const EVENT_CHANNEL: string = 'airplane_mode_checker_stream';

onAttachedToEngine(binding: FlutterPluginBinding): void {
  const messenger = binding.getBinaryMessenger();
  this.methodChannel = new MethodChannel(messenger, METHOD_CHANNEL);
  this.methodChannel.setMethodCallHandler(this);

  this.eventChannel = new EventChannel(messenger, EVENT_CHANNEL);
  this.eventChannel.setStreamHandler(new AirplaneModeStreamHandler(
    (events: EventSink, defaultValue: string): void => {
      this.startListening(events, defaultValue);
    },
    (): void => {
      this.stopListening();
    }));
}

6.2 读当前状态:settings.getValueSync

鸿蒙把系统设置项收在 @kit.BasicServicesKit 的 settings 里:

const SETTING_KEY: string = settings.general.AIRPLANE_MODE_STATUS;
const SETTING_ON_VALUE: string = '1';

// 需要 Context,所以插件要实现 AbilityAware
const value: string = settings.getValueSync(
  this.ability.context,
  SETTING_KEY,
  defaultValue        // 读不到时回退到调用方给的值,与上游语义一致
);
result.success(value === SETTING_ON_VALUE ? 'ON' : 'OFF');

这里有个必须说清楚的坑:settings.general.AIRPLANE_MODE_STATUS 在 API 21 起被标记为 deprecated(SDK 声明里写着),但它的替代品 @ohos.enterprise.deviceSettings 是系统应用/企业应用接口,普通三方应用根本用不了。

所以现状是:三方应用读飞行模式,目前仍只能走这一项 deprecated 接口。我在 README 的「已知限制」里如实记了这一条,而不是假装它很干净。

6.3 监听变化:系统公共事件比轮询更接近上游语义

安卓用 BroadcastReceiver,鸿蒙对应的机制是系统公共事件:

const AIRPLANE_MODE_EVENT: string = 'usual.event.AIRPLANE_MODE';

private startListening(events: EventSink, defaultValue: string): void {
  // 先推一次当前值,与上游「订阅即得到当前状态」的约定一致
  events.success(this.readCurrent(defaultValue));

  const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
    events: [AIRPLANE_MODE_EVENT],
  };
  this.subscriber = commonEventManager.createSubscriberSync(subscribeInfo);
  commonEventManager.subscribe(this.subscriber, (err, data) => {
    if (err) {
      Log.e(TAG, `subscribe failed: ${err.code} ${err.message}`);
      return;
    }
    events.success(this.readCurrent(defaultValue));
  });
}

用公共事件而不是定时轮询,有两个好处:一是实时,开关一动立刻推送;二是省电,没有轮询就有没有无谓的唤醒。

6.4 插件必须实现 AbilityAware

settings.getValueSync 需要 Context,而插件默认拿不到。所以和 system_settings_2 一样要实现 AbilityAware:

onAttachedToAbility(binding: AbilityPluginBinding): void {
  this.ability = binding.getAbility();
}
方法OpenHarmony 实现
getPlatformVersion()返回 OpenHarmony <版本>
checkAirplaneMode({defaultValue})settings.getValueSync(context, AIRPLANE_MODE_STATUS, defaultValue),'1' → ON
airplane_mode_checker_streamcommonEventManager.subscribe('usual.event.AIRPLANE_MODE'),订阅时先推一次当前值

七、构建配置与签名

这一步踩的坑与 system_settings_2 完全相同,两个都值得记下来。

7.1 compatibleSdkVersion 必须写 "26.0.0"

上游示例沿用旧模板 '5.1.0(18)',构建直接失败:

00306004 Specification Limit Violation
The project's compatibleSdkVersion: 18 cannot be lower than
the minimum compatible version 24 required by the dependencies: @ohos/flutter_ohos.

带括号的写法 '6.0.0(20)'、'26.0.0(26)' 都不对(后者报 00308018 Unknown Error)。正确写法是纯版本号:

{
  "products": [
    {
      "name": "default",
      "signingConfig": "default",
      "compatibleSdkVersion": "26.0.0",
      "targetSdkVersion": "26.0.0",
      "runtimeOS": "HarmonyOS"
    }
  ]
}

7.2 签名证书绑定了 bundleName

00303074 Configuration Error
The bundleName in app.json5/hvigorfile.ts does not match the bundleName
in the generated SigningConfigs.

调试证书按包名签发,本库示例原本是 com.u14h4i.airplane_mode_checker_example,与证书对应的 com.example.flutter_oh_demo 不一致。把 AppScope/app.json5 改成证书对应的包名即可。

八、通过 AtomGit 引入适配版

dependencies:
  airplane_mode_checker:
    git:
      url: https://atomgit.com/oh-flutter/airplane_mode_checker.git
      ref: 3.3.0-ohos-1.0.0-beta.1
flutter pub get

本库不需要任何权限声明——读取 AIRPLANE_MODE_STATUS 设置项和订阅 usual.event.AIRPLANE_MODE 公共事件都不需要申请权限。

九、构建、安装和真机验证

cd example
flutter pub get
flutter analyze
flutter build hap --debug
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.example.flutter_oh_demo

构建输出 ✓ Built build/ohos/hap/entry-default-signed.hap.,hdc 报告安装与启动成功。

真机上的操作顺序与结果:

操作实测显示
启动「平台版本:未查询 / 单次查询:未查询 / 事件通道:未订阅」
点「查平台版本」getPlatformVersion() → OpenHarmony,界面显示 OpenHarmony OpenHarmony-7.0.0.105
点「查飞行模式」checkAirplaneMode() → on,界面显示 ON(飞行模式已开)
点「订阅 / 取消订阅」开始订阅 listenAirplaneMode(),随后收到 事件 #1 → on,界面显示 订阅中(已收 1 次)· 最近事件:ON

订阅后立刻收到一次推送这一点很关键:它证明事件通道真的建立了,而不是只挂了个空 handler。

十、适配结果和当前边界

三个能力全部接通,Dart 层一行未改:

  • 平台版本查询:返回 OpenHarmony OpenHarmony-7.0.0.105(读的是设备真实版本,不是硬编码字符串);
  • 单次状态查询:真机返回 ON,与系统设置的飞行模式开关一致;
  • 事件订阅:订阅后立即推送当前值,之后每次开关变化都用系统公共事件推送。

已知边界:

  • AIRPLANE_MODE_STATUS 是 deprecated 接口:API 21 起被标记废弃,但替代品 @ohos.enterprise.deviceSettings 只对系统/企业应用开放,普通三方应用目前只能继续用它;
  • 测试设备当时处于飞行模式:真机上验证到的是 ON,OFF 分支的代码路径与 ON 完全对称(同一个 readCurrent 函数按 '1' 判断),但没有在同一台设备上截到 OFF 的画面;
  • 事件推送依赖系统公共事件:如果某版本系统不发布 usual.event.AIRPLANE_MODE,订阅会成功但收不到变化推送,此时单次查询仍然可用。

十一、FAQ

Q1:为什么不用轮询?

  • 现象: 定时调 checkAirplaneMode 也能拿到变化,但会持续唤醒应用。
  • 解决方法: 用系统公共事件 usual.event.AIRPLANE_MODE,只在真正变化时推送。
  • 验证结果: 订阅后立即收到一次当前值,说明通道建立正常。

Q2:defaultValue 参数是干什么的?

  • 现象: 上游 checkAirplaneMode({defaultValue}) 带一个默认值参数。
  • 解决方法: settings.getValueSync 的第三个参数就是「读不到时的返回值」,直接把 defaultValue 传进去,语义完全对齐。
  • 验证结果: 真机上读到真实值 '1',没有走到默认值分支。

Q3:为什么必须实现 AbilityAware?

  • 现象: settings.getValueSync 第一个参数是 Context,插件默认拿不到。
  • 解决方法: 实现 AbilityAware,在 onAttachedToAbility 里保存 UIAbility,再用 ability.context。
  • 验证结果: 正常宿主下三次调用全部成功。

Q4:和 system_settings_2 是什么关系?

  • 现象: 两者都跟飞行模式有关。
  • 解决方法: 本库是读状态 + 监听变化;system_settings_2 是打开设置页。想引导用户自己去开,用后者;想自己判断状态,用前者。
  • 验证结果: 两库在同一台设备上均可独立工作。

十二、总结

airplane_mode_checker 3.3.0 已完整接入鸿蒙:查询用 settings.getValueSync、监听用系统公共事件 usual.event.AIRPLANE_MODE,订阅时先推一次当前值以对齐上游语义。真机上读到 ON、订阅后收到 事件 #1 → on,Dart 层一行未改。

这个库最值得记的一条经验是:鸿蒙上有些「唯一可用」的接口本身是 deprecated 的。AIRPLANE_MODE_STATUS 就是典型——新接口只给系统应用,三方应用只能继续用旧的。遇到这种情况,正确做法是照用 + 在文档里写清楚,而不是因为「已废弃」就假装做不到。

十三、参考资料和仓库地址

欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter

Logo

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

更多推荐