环境搭建指引:https://atomgit.com/CPF-Flutter/flutter_samples/blob/master/ohos/getting-started/flutter-oh-env-setup.md

airplane_mode_checker 干两件事:读一次飞行模式,以及持续监听飞行模式的变化。它的 3.3.0 版支持 Android、iOS,没有 OpenHarmony。

它在鸿蒙上的适配形态很有代表性:安卓用 BroadcastReceiver 监听 ACTION_AIRPLANE_MODE_CHANGED,iOS 用 NSNotification,而鸿蒙对应的是系统公共事件 usual.event.AIRPLANE_MODE——从"广播"换到"公共事件",订阅/退订的时机、初值怎么给、失败怎么回落,都得一处处对齐。另外还有一个"坑得很真实"的点:读飞行模式的那个设置项已经从 API 21 起废弃,但替代品是系统/企业应用接口,普通三方应用根本用不了。

适配对象:上游 airplane_mode_checker 3.3.0(MIT);适配产物 TAG 3.3.0-ohos-1.0.0-beta.1。


一、这个库要解决什么

1.1 上游 API

// 平台版本
final String? version = await AirplaneModeChecker.instance.getPlatformVersion();

// 读一次:AirplaneModeStatus.on / .off
final AirplaneModeStatus status =
    await AirplaneModeChecker.instance.checkAirplaneMode();

// 监听变化(流的第一帧就是当前值)
AirplaneModeChecker.instance.listenAirplaneMode().listen((AirplaneModeStatus s) {
  print(s == AirplaneModeStatus.on ? '飞行模式已开' : '飞行模式已关');
});

1.2 契约:一条方法通道 + 一条事件通道

class MethodChannelAirplaneModeChecker extends AirplaneModeCheckerPlatform {
  final methodChannel = const MethodChannel('airplane_mode_checker');
  final eventChannel  = const EventChannel('airplane_mode_checker_stream');

  
  Future<String?> getPlatformVersion() async =>
      await methodChannel.invokeMethod<String>('getPlatformVersion');

  
  Future<String?> checkAirplaneMode({String defaultValue = 'OFF'}) async =>
      await methodChannel.invokeMethod<String>('checkAirplaneMode',
          <String, String>{'defaultValue': defaultValue});

  
  Stream<String> listenAirplaneMode({String defaultValue = 'OFF'}) {
    return eventChannel
        .receiveBroadcastStream(<String, String>{'defaultValue': defaultValue})
        .map((event) => event as String);
  }
}

两个细节决定了适配的形状:

  1. 两个方法都会带一个 defaultValue(默认 'OFF'),上游的语义是"拿不到就按这个值算";
  2. 事件通道的入参也带 defaultValue:receiveBroadcastStream({'defaultValue': 'OFF'}) 会把参数传给原生侧的 onListen。

Dart 层没有任何平台门,pubspec.yaml 只声明了 android / ios,所以鸿蒙侧接住这两条通道即可。

1.3 基线

node .agents/tools/tree-diff.mjs _probe/cand13/airplane_mode_checker _probe/amc_work
# 相同: 92  内容不同: 0  仅 B 有: .github / .gitignore / .metadata 等工程文件

上游 master(ce46686)与 pub.dev 上的 3.3.0 完全一致。


二、选库:四道筛 + 在线查重

筛子检查结果
① pub.dev 平台列表是否已含 ohos[android, ios] → 需要适配
② 兄弟包<lib>_ohos / pub.dev 上 airplane_mode_checker_ohos都没有
③ Dart 平台门Platform.is* / defaultTargetPlatform 分支无
④ 依赖体检dep-ohos-check.mjs airplane_mode_checkerdeps ok: plugin_platform_interface(pure)

在线查重(四组织全量快照 831 个仓库精确匹配):

---- airplane_mode_checker

干净。


三、六步适配流程

  1. atomgit.mjs create oh-flutter airplane_mode_checker "…" 建仓;
  2. 克隆:git clone https://gh-proxy.com/https://github.com/14h4i/airplane_mode_checker.git _probe/amc_work;
  3. 建分支 feat/ohos_airplane_mode_checker_3.3.0,跑 flutter create -t plugin --platforms ohos .;
  4. 写 ohos/src/main/ets/components/plugin/AirplaneModeCheckerPlugin.ets;
  5. 补三份 OpenHarmony 文档 + 根 README 说明 + 示例改自检台;
  6. 推送分支/main 并打 TAG,提交前清空 signingConfigs。

在这里插入图片描述


四、代码写在哪个文件

ohos/src/main/ets/components/plugin/AirplaneModeCheckerPlugin.ets   # 本篇唯一新增的实现文件

4.1 安卓广播 → 鸿蒙系统公共事件

const AIRPLANE_MODE_EVENT: string = 'usual.event.AIRPLANE_MODE';   // 常量名 COMMON_EVENT_AIRPLANE_MODE_CHANGED

const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
  events: [AIRPLANE_MODE_EVENT],
};
const subscriber = commonEventManager.createSubscriberSync(subscribeInfo);
commonEventManager.subscribe(subscriber, (err, data) => {
  if (err) { Log.e(TAG, `common event error code=${err.code}`); return; }
  const mode: string = this.readAirplaneMode(defaultValue);   // 事件到了就重读真实值
  Log.i(TAG, `common event ${data?.event} -> ${mode}`);
  events.success(mode);
});

退订对应 commonEventManager.unsubscribe(subscriber),在 onCancel 里调用——不留订阅这点和上游一样。

4.2 与上游对齐的两处语义

  1. onListen 先推一次当前值:上游安卓在 onListen 里就调 checkInitialAirplaneMode(),鸿蒙侧同样在订阅完成后立刻推一帧:
// 上游 onListen 里也是"注册完订阅立刻推一次初值"
const initial: string = this.readAirplaneMode(defaultValue);
Log.i(TAG, `stream listened, initial value = ${initial}`);
events.success(initial);
  1. 读不到就用调用方给的 defaultValue:checkAirplaneMode 和事件通道都遵守这条。

4.3 读值需要 Context:插件要实现 AbilityAware

onAttachedToAbility(binding: AbilityPluginBinding): void {
  this.ability = binding.getAbility();
}

private readAirplaneMode(fallback: string): string {
  const ability = this.ability;
  if (ability === null) { return fallback; }              // 拿不到 Ability 也要按上游语义回落
  try {
    const raw: string = settings.getValueSync(ability.context, SETTING_KEY, '1');
    return raw === '1' ? MODE_ON : MODE_OFF;
  } catch (error) {
    return fallback;
  }
}

4.4 AIRPLANE_MODE_STATUS 已废弃,但三方应用没有替代品

SDK 里 settings.general.AIRPLANE_MODE_STATUS 自 API 21 起 deprecated,替代者是 @ohos.enterprise.deviceSettings / @ohos.enterprise.restrictions——系统应用/企业应用接口,普通三方应用无权调用。所以三方应用读飞行模式目前仍只能用它;本实现继续用,并在读失败时回落 defaultValue。

事件侧没有这个问题:usual.event.AIRPLANE_MODE 是普通公共事件,订阅不需要特殊权限。

4.5 这次 flutter create 生成的模板垃圾

android/build.gradle.kts、android/settings.gradle.kts、example/android/**.gradle.kts、example/integration_test/、example/ios/Runner/SceneDelegate.swift、ios/airplane_mode_checker/Sources/.../PrivacyInfo.xcprivacy 等——删之前先 git ls-files <目录>:本库上游 iOS 用的是复数 Sources/,只删模板新增的那个文件,别整目录删(第 11 篇就因为整目录删把上游被跟踪的文件一起删了)。


五、真机(模拟器)验证

示例改成了自检台:三个按钮(查平台版本 / 查飞行模式 / 订阅-取消订阅),界面记录事件次数与最近事件,日志前缀 [AMC-CHECK],原生日志按 AirplaneModeCheckerPlugin 过滤。

项值
设备Pura 90 模拟器,HarmonyOS 6.1.1(24) / API 24,ohos-x64
构建flutter build hap --debug --target-platform ohos-x64
操作设备侧结果
getPlatformVersion()getPlatformVersion -> OpenHarmony OpenHarmony-6.1.1.125,Dart 侧拿到同一字符串
checkAirplaneMode()checkAirplaneMode -> ON (fallback=OFF) —— 该模拟器当前飞行模式为开,settings.getValueSync 返回 1 并按 "ON" 上报
订阅后立刻收到初值stream listened, initial value = ON;界面"事件通道"由"未订阅"变为"订阅中(已收 1 次)"
打开/关闭飞行模式后收到事件未验证(原因见下)
取消订阅后不再收事件未验证(同上)

为什么"变化事件"没验到(如实写明):这台模拟器的设置首页里没有可直接点到的飞行模式开关(emulator 那个 SIM 入口进去是空页/关于本机),设备上没有 settings 命令行工具(/bin/sh: settings: inaccessible or not found),而 hdc shell 的身份(uid=2000)没有权限修改系统设置。也就是说"制造一次飞行模式变化"这件事在模拟器上做不到——不是代码没跑通:事件路径与已通过的初值路径共用同一个 readAirplaneMode() + events.success(),只有触发源不同。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

验证环境说明:本轮原计划用 API 26 的 Pura X View(HarmonyOS 7.0.0 Beta2),但该镜像在本机反复崩溃(宿主侧堆损坏,存活 20–60 秒),因此完整验证在 API 24 的 Pura 90 上完成。API 26 上已确认插件注册与 ability 绑定正常(airplane_mode_checker channels registered / ability attached),按钮点击坐标需按 API 26 布局重测。


六、编译与构建踩坑

6.1 示例依赖与模板用例会拦住构建

  • fluttertoast:上游示例用它弹提示,鸿蒙上无实现 → 从示例移除,改成"界面 + [AMC-CHECK] 日志";
  • integration_test:SDK 包在鸿蒙侧没有对应模块,留在 dev_dependencies 会让 flutter build hap 报 AdaptorError 00303231: The srcPath is not a relative path: …/packages/integration_test/ohos;
  • example/test/widget_test.dart:断言上游那套 MyApp 界面(find.text('Check Airplane Mode')),与鸿蒙自检台不匹配 → 删除。插件自身的 13 个单测保留,flutter test → All tests passed!。

6.2 示例改写后的自检顺序

flutter analyze     # No issues found!
flutter test        # 13 个用例全通过
flutter build hap --debug --target-platform ohos-x64

七、已知限制

  • 读值接口已废弃:settings.general.AIRPLANE_MODE_STATUS 自 API 21 起 deprecated,三方应用无可用替代;
  • 不提供"设置飞行模式":本库 Dart API 只有读与监听,实现里也不调用需要系统权限的写接口;
  • "变化事件"未在设备上验到(原因见第五节),事件路径与初值路径同源;
  • 示例移除了 fluttertoast / integration_test,并删除了断言上游界面的 widget 测试。

八、常见问题

Q1:为什么鸿蒙要用"公共事件"监听飞行模式?
鸿蒙没有安卓那种可注册的广播;系统状态变化通过 SAMgr 的公共事件发布,usual.event.AIRPLANE_MODE 就是飞行模式开关变化对应的那一个(SDK 里常量名是 COMMON_EVENT_AIRPLANE_MODE_CHANGED)。

Q2:为什么插件需要 UIAbility?
settings.getValueSync(context, …) 需要 Context,插件默认只有 BinaryMessenger。所以实现 AbilityAware;拿不到 Ability 时也遵守上游语义回落 defaultValue,不抛异常。

Q3:settings.getValueSync 的第三个参数是什么?
默认值。实现里传 '1',所以只有明确读到 '0' 时才判定为关闭——这样"读不到"不会被误判成"飞行模式关"。

Q4:事件到了为什么还要重新读一次设置项?
公共事件只告诉你"变了",不保证带着新值。重新读一次 AIRPLANE_MODE_STATUS 才能拿到权威状态;这也和安卓实现一致(onReceive 里再查一次 Settings.Global.AIRPLANE_MODE_ON)。

Q5:订阅重复调用会怎样?
实现里对重复 onListen 做了保护:已经订阅时只补推一次当前值,不再重复 subscribe,避免事件被推多次、也避免退订时残留。

Q6:hdc shell 能不能直接改飞行模式来做验证?
不能。设备上没有 settings 命令行工具,且 hdc shell 的身份没有写系统设置的权限。要造变化只能在系统设置 UI 里点开关(本机模拟器恰好没有这个入口)。

Q7:平台版本返回什么?
OpenHarmony <deviceInfo.osFullName>。实测在 Pura 90 上是 OpenHarmony OpenHarmony-6.1.1.125。

Q8:需要声明权限吗?
读取与订阅都不需要权限声明。


九、本篇用到的库

项值
适配仓库https://atomgit.com/oh-flutter/airplane_mode_checker
上游仓库https://github.com/14h4i/airplane_mode_checker
上游版本3.3.0(MIT,master ce46686 与发布版逐文件一致)
适配 TAG3.3.0-ohos-1.0.0-beta.1
适配分支feat/ohos_airplane_mode_checker_3.3.0
通道方法通道 airplane_mode_checker、事件通道 airplane_mode_checker_stream
鸿蒙侧依赖@kit.BasicServicesKit(settings / commonEventManager / deviceInfo)、@kit.AbilityKit
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 for OpenHarmony SDK3.44.9+ohos-0.0.1-canary1(Dart 3.12.2)
DevEco Studio26.0.0.621
设备Pura 90 模拟器,HarmonyOS 6.1.1(24) / API 24,ohos-x64
构建产物example/build/ohos/hap/entry-default-signed.hap

复现命令

$env:PUB_CACHE = "E:\pub-cache"
cd _probe/amc_work/example/ohos
devecocli signature generate          # 首次需要;提交前清空 signingConfigs
cd ..
flutter build hap --debug --target-platform ohos-x64
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.u14h4i.airplane_mode_checker_example
# 依次点:查平台版本 → 查飞行模式 → 订阅/取消订阅
hdc shell snapshot_display -f /data/local/tmp/amc.jpeg
hdc file recv /data/local/tmp/amc.jpeg .
hdc shell hilog -x | Select-String "AirplaneModeCheckerPlugin"

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

Logo

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

更多推荐