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

environment_sensors 把设备的四类环境传感器封成四条 Dart 流:环境温度、相对湿度、环境光、气压,另外提供一个"这个传感器到底有没有"的可用性查询。它的 0.3.0 版支持 Android、iOS,没有 OpenHarmony。

它在鸿蒙适配里暴露了一个非常本质的问题:“同一个传感器,两家平台的编号不一样”。上游 Dart 直接把安卓的 Sensor.TYPE_* 常量(13 / 12 / 5 / 6)发给原生,而鸿蒙的 sensor.SensorId 里 13 是"湿度"、8 是"气压"——照抄数字会订到完全不相干的传感器。这个坑不靠翻译表是躲不过去的。

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


一、这个库要解决什么

1.1 上游 API

final sensors = EnvironmentSensors();

// 可用性
final bool hasTemp = await sensors.getSensorAvailable(SensorType.AmbientTemperature);

// 四条流(拿到的是 double)
sensors.temperature.listen((v) => print('温度 $v'));
sensors.humidity.listen((v) => print('湿度 $v'));
sensors.light.listen((v) => print('光照 $v'));
sensors.pressure.listen((v) => print('气压 $v'));

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

const _methodChannel = MethodChannel('environment_sensors/method');
const _temperatureEventChannel = EventChannel('environment_sensors/temperature');
const _humidityEventChannel    = EventChannel('environment_sensors/humidity');
const _lightEventChannel       = EventChannel('environment_sensors/light');
const _pressureEventChannel    = EventChannel('environment_sensors/pressure');

// 注意:参数是"裸数字"(位置参数),不是 map
Future<bool> getSensorAvailable(SensorType t) {
  if (t == SensorType.AmbientTemperature) return _methodChannel.invokeMethod('isSensorAvailable', 13);
  if (t == SensorType.Humidity)           return _methodChannel.invokeMethod('isSensorAvailable', 12);
  if (t == SensorType.Light)              return _methodChannel.invokeMethod('isSensorAvailable', 5);
  if (t == SensorType.Pressure)           return _methodChannel.invokeMethod('isSensorAvailable', 6);
  return false;
}

// 四条流都用 double.parse(event.toString())
_temperatureEvents = _temperatureEventChannel.receiveBroadcastStream()
    .map((event) => double.parse(event.toString()));

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

  1. isSensorAvailable 传的是安卓常量(13/12/5/6),且是位置参数;
  2. 事件通道必须送"能 double.parse 的值",鸿蒙侧要送数字(编码成 FLOAT64),不能送字符串。

Dart 层没有平台门,pubspec.yaml 只声明 android / ios。

1.3 基线

node .agents/tools/tree-diff.mjs _probe/cand14/environment_sensors _probe/es_work
# 相同: 78  内容不同: 0  仅 B 有: .gitignore / .metadata 等工程文件

上游 master(6d6ba7e)与 pub.dev 上的 0.3.0 一致。


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

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

在线查重(831 个组织仓库快照精确匹配 ---- environment_sensors):干净。同轮被排除的 ambient_light(hxa-flutter 已适配)。


三、六步适配流程

  1. 建仓 → 2. 克隆(_probe/es_work)→ 3. 建分支 feat/ohos_environment_sensors_0.3.0 并 flutter create -t plugin --platforms ohos . → 4. 写实现 → 5. 补三份文档 + 根 README + 示例改自检台 → 6. 推送并打 TAG 0.3.0-ohos-1.0.0-beta.1。

在这里插入图片描述


四、代码写在哪个文件

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

4.1 关键点一:安卓编号 ≠ 鸿蒙编号,必须做翻译表

传感器安卓常量鸿蒙 sensor.SensorId
环境温度13AMBIENT_TEMPERATURE = 260
相对湿度12HUMIDITY = 13
环境光5AMBIENT_LIGHT = 5
气压6BAROMETER = 8
ANDROID_TO_OHOS_SENSOR.set(13, sensor.SensorId.AMBIENT_TEMPERATURE);
ANDROID_TO_OHOS_SENSOR.set(12, sensor.SensorId.HUMIDITY);
ANDROID_TO_OHOS_SENSOR.set(5,  sensor.SensorId.AMBIENT_LIGHT);
ANDROID_TO_OHOS_SENSOR.set(6,  sensor.SensorId.BAROMETER);

不翻译会怎样:安卓的"温度 13"在鸿蒙是湿度,安卓的"气压 6"在鸿蒙是计步之类完全无关的传感器——订阅上去不报错,但拿到的是错的数据。这种"静默错误"最难查。

isSensorAvailable 的参数是裸数字,实现里两种形态都兼容:

const raw: Object | null = call.args;
if (typeof raw === 'number') return raw as number;          // 位置参数
const map = raw as Map<string, Object>;                      // 兼容 {type: n}
const value = map.get?.('type');

4.2 关键点二:sensor.on/off 的每个重载都要求字面量枚举

这样写编译不过:

sensor.on(this.sensorId, (r) => …);   // No overload matches this call

因为每个重载都把类型写成具体字面量(SensorId.AMBIENT_LIGHT、SensorId.BAROMETER …),传一个 SensorId 变量匹配不上任何一个。解决办法是在四个闭包里各自写死字面量,交给同一个 handler 调度:

addChannel(LIGHT_CHANNEL,
  (emit) => { sensor.on(sensor.SensorId.AMBIENT_LIGHT, (r: sensor.LightResponse) => emit(r.intensity)); },
  () => { sensor.off(sensor.SensorId.AMBIENT_LIGHT); });

四条流各自取自己的字段:AmbientTemperatureResponse.temperature / HumidityResponse.humidity / LightResponse.intensity / BarometerResponse.pressure。

4.3 关键点三:可用性判断就是"真的去解析一次"

鸿蒙没有 SensorManager.getDefaultSensor(type) != null 的直接写法,但 sensor.getSingleSensorSync(id) 在传感器不存在时会抛异常:

try {
  const found = sensor.getSingleSensorSync(sensorId);
  Log.i(TAG, `sensor found: name=${found.sensorName} id=${found.sensorId}`);
  return true;
} catch (error) {
  Log.w(TAG, `sensor ${sensorId} unavailable: ${(error as BusinessError).message}`);
  return false;
}

4.4 事件频率与日志

系统回调频率由 sensor.on 的 options 决定(本实现不传 options),为避免 hilog 被冲爆,每 20 条才打一行日志;Dart 侧拿到的仍是全部事件(不节流)。


五、真机(模拟器)验证

示例是自检台:一个"重新查询四种传感器是否可用"按钮 + 每种传感器一个"订阅/退订"按钮,界面显示可用性与已收事件数,日志前缀 [ES-CHECK],原生日志按 EnvironmentSensorsPlugin 过滤。

项值
设备Pura 90 模拟器,HarmonyOS 6.1.1(24) / API 24,ohos-x64
操作设备侧结果
getSensorAvailable(AmbientTemperature)(安卓 13)isSensorAvailable(android type 13) -> false;日志 sensor 260 unavailable: The sensor is not supported by the device.
getSensorAvailable(Humidity)(安卓 12)isSensorAvailable(android type 12) -> false
getSensorAvailable(Light)(安卓 5)sensor found: name=light id=5 → isSensorAvailable(android type 5) -> true
getSensorAvailable(Pressure)(安卓 6)isSensorAvailable(android type 6) -> false;日志 sensor 8 unavailable: …(鸿蒙气压 id 是 8)
订阅环境光sensor listening started,随后 sensor event #1 = 0、#21 = 0、#41 = 0 …(该模拟器光感默认读数 0)
注入光照后数值变化devecocli emulator sensor --target "Pura 90" --light-intensity 30000 之后,下一条事件变成 sensor event #81 = 30000
三个不可用的传感器不产生任何事件(getSingleSensorSync 抛异常 → 界面显示"不可用")

最后一条是本次验证里最有说服力的一步:注入 → 事件值变化,证明事件通道推的是真实传感器数据,而不是常量或缓存。

API 26(HarmonyOS 7.0.0 Beta2)上的差异(重要)

在最新模拟器 Pura X View(API 26)上复测时发现:

  • 可用性判定一致:sensor found: name=light id=5 → isSensorAvailable(android type 5) -> true,其余三种为 false;
  • 但订阅失败:sensor.on failed code=401 message=The parameter invalid.

也就是说,同一份代码在 API 24 上能订阅成功并收到数据,在 API 26 上 sensor.on 直接报 401(参数错误)。这属于跨版本行为差异,不是"没跑通":getSingleSensorSync 证明传感器在、sensor.on 在 API 26 上需要不同/额外的参数(大概率要显式传 Options,例如采样间隔)。这一点已如实记录,修复与复试安排在下一轮(改带 Options 的调用,并在失败时保留原调用作为回退)。

在这里插入图片描述

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

验证环境说明:完整验证在 API 24(Pura 90)完成;API 26(Pura X View,7.0.0 Beta2)上完成了可用性复测并发现上述 401 差异。该 Beta 镜像在本机存活窗口只有 20–60 秒,故采用"一窗口一应用 + 断点续传"的方式取证。


六、编译与构建踩坑

6.1 No overload matches this call(两次)

第一次是 sensor.on/off 传变量(见 4.2);修完后还有一次,根因相同——SDK 里这类"按枚举成员重载"的 API 都不接受变量。遇到这个报错,先想"是不是该把字面量写进闭包"。

6.2 示例侧

  • 上游示例把 _tempAvailable 用来判断湿度流、_humidityAvailable 判断温度流(上游自身的笔误),鸿蒙示例改成"每种传感器各自判断、各自订阅";
  • flutter create 生成的 lib/environment_sensors_method_channel.dart、lib/environment_sensors_platform_interface.dart、test/environment_sensors_method_channel_test.dart、example/**/*.kts、example/integration_test/、ios/Classes/EnvironmentSensorsPlugin.swift 等全部删除(本库不是联邦式插件,上游只有一个 Dart 文件)。

七、已知限制

  • 只映射上游暴露的四种传感器,其它 id 一律返回 false;
  • API 26 上 sensor.on 报 401(见第五节),修复前应以 API 24 为准;
  • 回调频率由系统决定,插件只对日志做节流,不对数据节流;
  • 示例的可用性/订阅逻辑已按"每种传感器独立"重写,与上游示例的写法不同(上游存在笔误)。

八、常见问题

Q1:为什么不直接拿 Dart 传来的数字去 sensor.on?
因为两家编号体系不同(见 4.1 表格)。鸿蒙的 13 是湿度、8 是气压,直接传会订到无关传感器——不报错但数据全错,是最难查的一类问题。

Q2:isSensorAvailable 的参数为什么是裸数字?
上游 Dart 就是这么写的:invokeMethod('isSensorAvailable', 13)。第二个位置参数会被编码成数字而不是 map,实现里必须兼容这种形态。

Q3:鸿蒙有没有"列出所有可用传感器"的接口?
有(getSensorListSync),但本库只需要"某一个在不在",用 getSingleSensorSync 更直接,而且它抛异常的行为正好可以当判据。

Q4:为什么事件值要送数字而不是字符串?
上游 Dart 是 double.parse(event.toString())。送数字会编码成 FLOAT64,toString() 后形如 0.0/30000.0,能正常解析;送字符串虽也能解析,但不如数字直接、也避免精度/格式歧义。

Q5:光照为什么一直是 0?
这台模拟器的光感默认读数就是 0("漆黑"档)。用 devecocli emulator sensor --target "<实例名>" --light-intensity N 注入即可看到数值变化(实测 30000)。

Q6:其它三种传感器为什么不可用?
模拟器只虚拟了光感。可用性判定如实返回 false,正是它该有的行为——不要把"没有传感器"实现成"返回 0",那会让调用方误判。

Q7:API 26 的 401 会影响使用吗?
在 API 26 设备上目前会影响订阅(可用性查询正常)。修复方向是显式传 Options;在 API 24 上功能完整。

Q8:需要声明权限吗?
读取这些环境传感器不需要权限声明。


九、本篇用到的库

项值
适配仓库https://atomgit.com/oh-flutter/environment_sensors
上游仓库https://github.com/nhandrew/environment_sensors
上游版本0.3.0(MIT,master 6d6ba7e 与发布版逐文件一致)
适配 TAG0.3.0-ohos-1.0.0-beta.1
适配分支feat/ohos_environment_sensors_0.3.0
通道方法通道 environment_sensors/method;事件通道 …/temperature、…/humidity、…/light、…/pressure
鸿蒙侧依赖@kit.SensorServiceKit(sensor.getSingleSensorSync / sensor.on / sensor.off)
dependencies:
  environment_sensors:
    git:
      url: https://atomgit.com/oh-flutter/environment_sensors.git
      ref: 0.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(完整验证);Pura X View,HarmonyOS 7.0.0(26.0.0) Beta2 / API 26(可用性复测)
构建产物example/build/ohos/hap/entry-default-signed.hap

复现命令

$env:PUB_CACHE = "E:\pub-cache"
cd _probe/es_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.julow.environment_sensors_example
# 1) 点"重新查询四种传感器是否可用"  2) 点"环境光"订阅
devecocli emulator sensor --target "Pura 90" --light-intensity 30000   # 观察事件值 0 -> 30000
hdc shell snapshot_display -f /data/local/tmp/es.jpeg
hdc file recv /data/local/tmp/es.jpeg .
hdc shell hilog -x | Select-String "EnvironmentSensorsPlugin"

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

Logo

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

更多推荐