做运动手表应用的时候,有个问题在模拟器上永远复现不了:用户退出运动页面,半小时后手表烫得厉害,电量掉了百分之十几。日志里传感器回调还在刷,心率、加速度的数据一行行往外蹦。你以为是系统没回收进程,实际上是你自己没退订——HarmonyOS 的传感器订阅是“谁订谁退”的模型,页面销毁不会自动帮你调 off。穿戴设备的功耗预算比手机紧得多,一次遗漏退订,在手表上就是肉眼可见的电量流失。

on 和 off 必须成对,而且 off 的回调引用要存住

传感器模块的订阅模型很直接:sensor.on(SensorId.HEART_RATE, callback) 开始上报,sensor.off(SensorId.HEART_RATE, callback) 停止上报。坑在于 off 必须传同一个回调函数引用。如果你在 on 里写匿名箭头函数,off 里再写一个“看起来一样”的箭头函数,解绑的是空气,底层订阅纹丝不动。回调要存成字段,off 时把同一个引用传回去。

typescript

import { sensor } from '@kit.SensorServiceKit';

class HeartRateSession {
  private callback: sensor.Callback<sensor.HeartRateResponse> | undefined;
  private active: boolean = false;

  start(): void {
    if (this.active) { return; }
    this.callback = (data: sensor.HeartRateResponse) => {
      console.info(`hr: ${data.heartRate}`);
    };
    sensor.on(sensor.SensorId.HEART_RATE, this.callback, { interval: 1000000000 });
    this.active = true;
  }

  stop(): void {
    if (!this.active || !this.callback) { return; }
    sensor.off(sensor.SensorId.HEART_RATE, this.callback);
    this.callback = undefined;
    this.active = false;
  }
}

sensor.off 的第二个参数必须是 start 里存下来的那个 this.callback。官方文档明确写了“订阅与取消订阅接口成对调用”,同一传感器多次订阅只有最后一次生效。如果你在页面的 onPageShow 里每次 start,却不先检查 active,回调会被覆盖,旧的回调再也无法通过 off 找回。

页面可见性和传感器采样频率要解耦

手表页面有一个特殊性:屏幕熄灭或者表盘被覆盖时,页面仍然“存在”,但用户已经看不到数据了。如果传感器还在以高频上报,UI 线程还在处理回调,电量和帧率同时受损。正确做法是把采样频率和页面可见性绑定:onPageShow 时启动,onPageHide 时降频或暂停,aboutToDisappear 时彻底 stop。社区里有一个可复用的判定模型——把订阅封装成带引用计数的 Session,acquire 和 release 成对出现,release 到 0 时才真正退订。

频率不是越高越好,从业务可感知的变化反推最低值

官方 Options 的 interval 默认是 200000000ns(约 0.2 秒),但很多开发者直接忽略这个参数,任由默认值跑。运动姿态页面可能需要 20Hz 以上,但心率展示每秒更新一次就够了。用 interval: 1000000000(1 秒)和默认值相比,CPU 唤醒次数差一个数量级。穿戴设备的功耗预算不允许“反正传感器支持高频率我就用高频率”这种思路,采样频率应该从“用户能感知到的最小变化”倒推,而不是从传感器规格上限往下砍。

排查这类问题的顺序很固定:先看 off 有没有传同一个回调,再看 on 和 off 是否严格成对,最后用 DevEco Profiler 的 CPU 和能耗面板对比“页面退出后是否还有传感器线程活跃”。一个 off 漏掉,在手表上就是几小时的续航差距。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐