背景
在 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。- import { sensor } from '@kit.SensorServiceKit';
- 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。- export interface CompassResult {
- heading: number
- errMsg: string
- }
- export interface CompassOptions {
- interval?: number
- success?: (res: CompassResult) => void
- fail?: (res: any) => void
- complete?: (res: any) => void
- }
- export type StartCompass = (options: CompassOptions) => void
- export type StopCompass = () => void
- export type CompassErrorCode = 9240001 | 9240002
- export interface CompassFail extends IUniError {
- errCode: CompassErrorCode
- }
复制代码
三、核心实现
app-harmony/index.uts 中的逻辑不复杂。用一个模块级变量 isListening 做防重复处理:如果已经处于监听状态,startCompass 直接 return;订阅成功后把 isListening 置为 true。stopCompass 中判断 isListening,再调用 sensor.off 取消订阅,并把标志位重置为 false。- let isListening = false;
- export const startCompass: StartCompass = function (options: CompassOptions) {
- if (isListening) {
- return;
- }
- try {
- const interval = options.interval ?? 200;
- sensor.on(sensor.SensorId.ORIENTATION, (data: sensor.OrientationResponse) => {
- const heading = Math.round(data.alpha);
- const res: CompassResult = {
- heading: heading,
- errMsg: 'startCompass:ok'
- };
- options.success?.(res);
- options.complete?.(res);
- }, { interval: interval * 1000000 });
- isListening = true;
- } catch (e) {
- const err = e as BusinessError;
- const failErr = new CompassFailImpl(9240002);
- failErr.errMsg = '设备不支持罗盘传感器: ' + err.message;
- options.fail?.(failErr);
- options.complete?.(failErr);
- }
- }
- export const stopCompass: StopCompass = function () {
- if (isListening) {
- sensor.off(sensor.SensorId.ORIENTATION);
- isListening = false;
- }
- }
复制代码 从实现可以看到,成功路径会调用 success 和 complete,异常路径会调用 fail 和 complete,并且异常时使用 9240002 上报设备不支持罗盘传感器。这里也说明接口里虽然定义了 9240001,但当前 catch 分支主要覆盖的是不支持传感器这一类错误。
四、三个踩坑与修复
坑1:interval 单位搞错。最初以为 options.interval 单位是毫秒,直接传 200,结果传感器数据疯狂上报,CPU 占用飙升,设备发热明显。后来查文档确认单位是纳秒,200 纳秒相当于每 0.0002 毫秒上报一次数据。正确做法是毫秒值乘以 1000000 转成纳秒。- sensor.on(sensor.SensorId.ORIENTATION, callback, { interval: 200 }); // 错误
- sensor.on(sensor.SensorId.ORIENTATION, callback, { interval: 200 * 1000000 }); // 正确
复制代码 坑2:忘记取消订阅。页面销毁时没有调用 stopCompass,传感器一直在后台运行。切换几次页面后,设备电量掉得特别快。正确做法是在页面 onUnload 生命周期里调用 stopCompass,确保 sensor.off 被执行。- export default {
- methods: {
- startCompass() {
- startCompass({ success: (res) => { ... } })
- },
- stopCompass() {
- stopCompass()
- }
- },
- onUnload() {
- this.stopCompass()
- }
- }
复制代码 坑3:重复调用 startCompass。用户快速点击“开始监听”按钮时,startCompass 可能被调用多次,传感器被重复订阅,回调执行多遍,界面上的角度值跳动异常。加上 isListening 标志位后,已经在监听时直接 return,避免重复订阅。这个防重逻辑与取消订阅逻辑配套使用,才能保证生命周期内状态一致。
五、示例页面与方向映射
示例页面做了一个简单的指南针 UI:中间是圆盘,红色指针根据方向角度旋转;下方显示方向角度和方向文本,并提供开始监听、停止监听按钮和状态文本。指针旋转使用 CSS transform,角度取负值,因为罗盘指针要反向旋转才能正确指向北方。方向映射把 360 度分成 8 个区间:- getDirection(heading: number): string {
- if (heading >= 337.5 || heading < 22.5) return '北'
- if (heading >= 22.5 && heading < 67.5) return '东北'
- if (heading >= 67.5 && heading < 112.5) return '东'
- if (heading >= 112.5 && heading < 157.5) return '东南'
- if (heading >= 157.5 && heading < 202.5) return '南'
- if (heading >= 202.5 && heading < 247.5) return '西南'
- if (heading >= 247.5 && heading < 292.5) return '西'
- if (heading >= 292.5 && heading < 337.5) return '西北'
- return '北'
- }
复制代码
六、总结
电子罗盘插件的核心就是调用 sensor.on 订阅 ORIENTATION 传感器,从回调的 alpha 属性获取方向角度。开发时需要重点检查三件事:interval 单位是纳秒,毫秒转纳秒要乘 1000000;页面销毁时调用 stopCompass 取消订阅;startCompass 做好防重复调用。把这些点处理好,罗盘功能在鸿蒙侧的上报频率、功耗和回调次数都能保持可控。 |