查看: 110|回复: 0

鸿蒙电子罗盘插件开发:ORIENTATION 传感器订阅与避坑

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
背景
在 uniappx 鸿蒙插件项目里做一个指南针功能,目标是让用户实时看到设备朝向。最初考虑用加速度计和磁力计自己算角度,后来发现鸿蒙 SensorServiceKit 已经提供 ORIENTATION 传感器,回调直接返回方向角度,能省去大量数学计算。下面按 API 调研、接口设计、核心实现、踩坑和示例页面,把这次电子罗盘插件的开发过程整理一遍。

一、SensorServiceKit 的 ORIENTATION 传感器
官方文档确认,方向传感器订阅依赖 @kit.SensorServiceKit,异常类型来自 @kit.BasicServicesKit。核心 API 只有两个:
sensor.on(sensor.SensorId.ORIENTATION, callback, options) 用于订阅方向传感器数据;sensor.off(sensor.SensorId.ORIENTATION) 用于取消订阅。
回调参数是 OrientationResponse,其中的 alpha 表示设备绕 Z 轴旋转的角度,范围 0-360 度,0 度代表正北。这个值可以直接作为罗盘方向角使用。采样间隔通过 options.interval 设置,单位是纳秒。例如需要 200ms 采样一次,应传 200 * 1000000。
  1. import { sensor } from '@kit.SensorServiceKit';
  2. import { BusinessError } from '@kit.BasicServicesKit';
复制代码

二、插件接口与错误码设计
interface.uts 中定义罗盘数据结果、选项、函数签名和错误码。CompassResult 包含 heading(0-360 度,0 为正北)和 errMsg。CompassOptions 支持 interval(毫秒,默认 200)以及 success、fail、complete 回调。StartCompass、StopCompass 作为对外函数签名。错误码定义了两个:9240001 表示罗盘初始化失败,9240002 表示设备不支持罗盘传感器。CompassFail 继承 IUniError,并携带 errCode。
  1. export interface CompassResult {
  2.   heading: number
  3.   errMsg: string
  4. }
  5. export interface CompassOptions {
  6.   interval?: number
  7.   success?: (res: CompassResult) => void
  8.   fail?: (res: any) => void
  9.   complete?: (res: any) => void
  10. }
  11. export type StartCompass = (options: CompassOptions) => void
  12. export type StopCompass = () => void
  13. export type CompassErrorCode = 9240001 | 9240002
  14. export interface CompassFail extends IUniError {
  15.   errCode: CompassErrorCode
  16. }
复制代码

三、核心实现
app-harmony/index.uts 中的逻辑不复杂。用一个模块级变量 isListening 做防重复处理:如果已经处于监听状态,startCompass 直接 return;订阅成功后把 isListening 置为 true。stopCompass 中判断 isListening,再调用 sensor.off 取消订阅,并把标志位重置为 false。
  1. let isListening = false;
  2. export const startCompass: StartCompass = function (options: CompassOptions) {
  3.   if (isListening) {
  4.     return;
  5.   }
  6.   try {
  7.     const interval = options.interval ?? 200;
  8.     sensor.on(sensor.SensorId.ORIENTATION, (data: sensor.OrientationResponse) => {
  9.       const heading = Math.round(data.alpha);
  10.       const res: CompassResult = {
  11.         heading: heading,
  12.         errMsg: 'startCompass:ok'
  13.       };
  14.       options.success?.(res);
  15.       options.complete?.(res);
  16.     }, { interval: interval * 1000000 });
  17.     isListening = true;
  18.   } catch (e) {
  19.     const err = e as BusinessError;
  20.     const failErr = new CompassFailImpl(9240002);
  21.     failErr.errMsg = '设备不支持罗盘传感器: ' + err.message;
  22.     options.fail?.(failErr);
  23.     options.complete?.(failErr);
  24.   }
  25. }
  26. export const stopCompass: StopCompass = function () {
  27.   if (isListening) {
  28.     sensor.off(sensor.SensorId.ORIENTATION);
  29.     isListening = false;
  30.   }
  31. }
复制代码
从实现可以看到,成功路径会调用 success 和 complete,异常路径会调用 fail 和 complete,并且异常时使用 9240002 上报设备不支持罗盘传感器。这里也说明接口里虽然定义了 9240001,但当前 catch 分支主要覆盖的是不支持传感器这一类错误。

四、三个踩坑与修复
坑1:interval 单位搞错。最初以为 options.interval 单位是毫秒,直接传 200,结果传感器数据疯狂上报,CPU 占用飙升,设备发热明显。后来查文档确认单位是纳秒,200 纳秒相当于每 0.0002 毫秒上报一次数据。正确做法是毫秒值乘以 1000000 转成纳秒。
  1. sensor.on(sensor.SensorId.ORIENTATION, callback, { interval: 200 }); // 错误
  2. sensor.on(sensor.SensorId.ORIENTATION, callback, { interval: 200 * 1000000 }); // 正确
复制代码
坑2:忘记取消订阅。页面销毁时没有调用 stopCompass,传感器一直在后台运行。切换几次页面后,设备电量掉得特别快。正确做法是在页面 onUnload 生命周期里调用 stopCompass,确保 sensor.off 被执行。
  1. export default {
  2.   methods: {
  3.     startCompass() {
  4.       startCompass({ success: (res) => { ... } })
  5.     },
  6.     stopCompass() {
  7.       stopCompass()
  8.     }
  9.   },
  10.   onUnload() {
  11.     this.stopCompass()
  12.   }
  13. }
复制代码
坑3:重复调用 startCompass。用户快速点击“开始监听”按钮时,startCompass 可能被调用多次,传感器被重复订阅,回调执行多遍,界面上的角度值跳动异常。加上 isListening 标志位后,已经在监听时直接 return,避免重复订阅。这个防重逻辑与取消订阅逻辑配套使用,才能保证生命周期内状态一致。

五、示例页面与方向映射
示例页面做了一个简单的指南针 UI:中间是圆盘,红色指针根据方向角度旋转;下方显示方向角度和方向文本,并提供开始监听、停止监听按钮和状态文本。指针旋转使用 CSS transform,角度取负值,因为罗盘指针要反向旋转才能正确指向北方。方向映射把 360 度分成 8 个区间:
  1. getDirection(heading: number): string {
  2.   if (heading >= 337.5 || heading < 22.5) return '北'
  3.   if (heading >= 22.5 && heading < 67.5) return '东北'
  4.   if (heading >= 67.5 && heading < 112.5) return '东'
  5.   if (heading >= 112.5 && heading < 157.5) return '东南'
  6.   if (heading >= 157.5 && heading < 202.5) return '南'
  7.   if (heading >= 202.5 && heading < 247.5) return '西南'
  8.   if (heading >= 247.5 && heading < 292.5) return '西'
  9.   if (heading >= 292.5 && heading < 337.5) return '西北'
  10.   return '北'
  11. }
复制代码

六、总结
电子罗盘插件的核心就是调用 sensor.on 订阅 ORIENTATION 传感器,从回调的 alpha 属性获取方向角度。开发时需要重点检查三件事:interval 单位是纳秒,毫秒转纳秒要乘 1000000;页面销毁时调用 stopCompass 取消订阅;startCompass 做好防重复调用。把这些点处理好,罗盘功能在鸿蒙侧的上报频率、功耗和回调次数都能保持可控。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-9-22 14:12 , Processed in 0.024667 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部