传感器与硬件访问:你想读硬件?硬件同意了吗?

举报
bug菌 发表于 2025/12/25 17:04:46 2025/12/25
【摘要】 🏆本文收录于「滚雪球学SpringBoot」专栏(全网一个名),手把手带你零基础入门Spring Boot,从入门到就业,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8 摘要这一章我可太有“硬件情结”了😆——传感器、定位、蓝牙、Wi-Fi...

🏆本文收录于「滚雪球学SpringBoot」专栏(全网一个名),手把手带你零基础入门Spring Boot,从入门到就业,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!

环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8

摘要

这一章我可太有“硬件情结”了😆——传感器、定位、蓝牙、Wi-Fi这四兄弟,写好了像开挂🚀;写不好嘛……就会出现那种“我明明开了权限,为啥它还装死😵‍💫?”的名场面。来,咱把它讲得又稳又好玩,还能直接抄进项目里用✅

🧭目录(先把路牌立好🪧)

  • 🏃‍♂️ 20.1 Sensor API:加速度 & 陀螺仪(动起来!)
  • 📍 20.2 位置服务:单次定位 & 持续监听(省电才是王道)
  • 🔵 20.3 蓝牙(BLE):扫描、过滤、事件回调(别扫成雷达站)
  • 📶 20.4 Wi-Fi:状态、已连接信息、事件监听(别乱扫,容易挨骂)
  • 🧯 20.5 常见翻车点(我替你踩过😭)

🏃‍♂️20.1 Sensor API(加速度、陀螺仪)——“手机在动”,你得听得懂🫨

✅你能用 Sensor API 干啥?

  • 加速度 ACCELEROMETER:看设备在三轴上的加速度(含重力),做运动/甩一甩/游戏重力感应都靠它
  • 陀螺仪 GYROSCOPE:看角速度(rad/s),做转向、体感、姿态判断特别香

官方的订阅接口很清晰:sensor.on / sensor.once / sensor.off,订阅和取消订阅要成对出现,不然就会一直上报、一直耗电(你手机:我谢谢你😅)

🔐权限怎么搞?

加速度/陀螺仪对应权限在文档里写得明明白白:

  • ohos.permission.ACCELEROMETER
  • ohos.permission.GYROSCOPE
    而且它们属于 system_grant(系统授权型),通常不需要你弹窗请求,但必须在 module.json5 里声明

📌module.json5 权限声明示例(别偷懒哈😤)

{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.ACCELEROMETER" },
      { "name": "ohos.permission.GYROSCOPE" }
    ]
  }
}

声明权限的规范(reason/usedScene 等)官方有明确要求,尤其是 user_grant 类权限更严格

🧪实战:加速度 + 陀螺仪实时面板(带“低通滤波”,不抖成鬼畜👻)

我这个示例特别适合你复制进项目里:
进入页面开始监听 → 离开页面立刻取消监听,不当“电量刺客”🪫

import sensor from '@ohos.sensor'

@Entry
@Component
struct MotionDashboard {
  @State ax: number = 0
  @State ay: number = 0
  @State az: number = 0

  @State gx: number = 0
  @State gy: number = 0
  @State gz: number = 0

  // 低通滤波系数:越小越稳,越大越灵敏(也越抖😅)
  private alpha: number = 0.2

  private accCb = (data: sensor.Response) => {
    // data.x / data.y / data.z
    this.ax = this.ax + this.alpha * (data.x - this.ax)
    this.ay = this.ay + this.alpha * (data.y - this.ay)
    this.az = this.az + this.alpha * (data.z - this.az)
  }

  private gyrCb = (data: sensor.Response) => {
    this.gx = this.gx + this.alpha * (data.x - this.gx)
    this.gy = this.gy + this.alpha * (data.y - this.gy)
    this.gz = this.gz + this.alpha * (data.z - this.gz)
  }

  aboutToAppear() {
    // 持续监听:sensor.on / 取消:sensor.off :contentReference[oaicite:3]{index=3}
    sensor.on(sensor.SensorId.ACCELEROMETER, this.accCb)
    sensor.on(sensor.SensorId.GYROSCOPE, this.gyrCb)
  }

  aboutToDisappear() {
    sensor.off(sensor.SensorId.ACCELEROMETER, this.accCb)
    sensor.off(sensor.SensorId.GYROSCOPE, this.gyrCb)
  }

  build() {
    Column({ space: 12 }) {
      Text('🧲 运动传感器面板').fontSize(22).fontWeight(FontWeight.Bold)

      Column({ space: 6 }) {
        Text('🏃‍♂️ 加速度 (m/s²)').fontSize(16).fontWeight(FontWeight.Medium)
        Text(`x=${this.ax.toFixed(2)}  y=${this.ay.toFixed(2)}  z=${this.az.toFixed(2)}`)
      }.padding(12).borderRadius(12).backgroundColor('#F6F6F6')

      Column({ space: 6 }) {
        Text('🌀 陀螺仪 (rad/s)').fontSize(16).fontWeight(FontWeight.Medium)
        Text(`x=${this.gx.toFixed(2)}  y=${this.gy.toFixed(2)}  z=${this.gz.toFixed(2)}`)
      }.padding(12).borderRadius(12).backgroundColor('#F6F6F6')

      Text('😄 小提示:离开页面记得 off,不然你手机会“悄悄发热”给你看。')
        .fontColor('#666')
    }
    .padding(16)
  }
}

📍20.2 位置服务——“我在哪儿”可以问,但别一直问🙃

位置能力由 geoLocationManager 提供,官方把常用接口都列得很清楚:

  • isLocationEnabled():位置开关是否开了
  • getLastLocation():取最近一次缓存位置(更省电)
  • getCurrentLocation():取当前位置(更实时,功耗更大)
  • on('locationChange', …) / off('locationChange', …):持续监听位置变化

我最喜欢的省电套路就是:

先拿 getLastLocation(有缓存就直接用),觉得不够新鲜再 getCurrentLocation。官方也建议优先用缓存位置以降低功耗

🔐权限:定位是 user_grant,别想“偷偷来”😅

声明权限要按规范写进 module.json5,文档强调:不声明可能拿不到授权,甚至上架会被拦

📌module.json5(定位权限示例:模糊 + 精确)

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:approx_location_reason",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "$string:precise_location_reason",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      }
    ]
  }
}

声明规范(reason/usedScene 必填的场景、写法等)见官方指南

🧪实战:一次定位(先缓存后实时)+ 权限弹窗申请✅

权限申请用 abilityAccessCtrl.requestPermissionsFromUser(AtManager)是标准做法

import { geoLocationManager } from '@kit.LocationKit'
import abilityAccessCtrl from '@ohos.abilityAccessCtrl'
import { promptAction } from '@kit.ArkUI'

async function ensureLocationPermission(context: any): Promise<boolean> {
  const atManager = abilityAccessCtrl.createAtManager()
  const res = await atManager.requestPermissionsFromUser(context, [
    'ohos.permission.APPROXIMATELY_LOCATION',
    'ohos.permission.LOCATION'
  ]) // requestPermissionsFromUser :contentReference[oaicite:9]{index=9}
  // res.authResults: 每个权限的授权结果(具体字段以实际返回为准)
  return res.authResults?.every(r => r === 0) ?? false
}

async function getNiceLocation(context: any) {
  // 位置开关判断 :contentReference[oaicite:10]{index=10}
  const enabled = geoLocationManager.isLocationEnabled()
  if (!enabled) {
    promptAction.showToast({ message: '📍 位置开关没开呀…先去设置里开一下🥲' })
    return
  }

  const ok = await ensureLocationPermission(context)
  if (!ok) {
    promptAction.showToast({ message: '🙃 你不给权限,我也没办法假装知道你在哪儿…' })
    return
  }

  try {
    // 省电优先:最近缓存位置 :contentReference[oaicite:11]{index=11}
    const last = geoLocationManager.getLastLocation()
    if (last) return last
  } catch (e) {
    // 没缓存很正常,别慌😄
  }

  // 实时定位:更耗电,但更“新鲜” :contentReference[oaicite:12]{index=12}
  const current = await geoLocationManager.getCurrentLocation()
  return current
}

🔵20.3 蓝牙(BLE)——扫描可以,但别扫成“电量收割机”🪫😅

BLE 这块我先送你一句“老江湖忠告”😎:

扫描必须加过滤条件。不然你会把附近所有设备都扫出来(而且功耗蹭蹭涨)。

官方 BLE 模块列得很全:

  • ble.startBLEScan / ble.stopBLEScan
  • ble.on('BLEDeviceFind') / ble.off('BLEDeviceFind')
    还特别提醒:filters 传 null 会扫到一堆非预期设备、并增加功耗(这不是吓你,是官方原话意思😅)

🔐蓝牙权限小坑:别用过期权限名

官方 FAQ 讲得很直白:USE_BLUETOOTH 属于旧时代(API 7/8/9)用的权限,后来推荐用 ACCESS_BLUETOOTH 等(别抱着“旧钥匙开新门”啊🤣)
社区实践里也明确提到扫描前需获得如 ohos.permission.ACCESS_BLUETOOTH 等权限

🧪实战:BLE 扫描 + 过滤 + 事件回调(只扫我想要的😏)

import { ble } from '@kit.ConnectivityKit'

type Found = { name?: string, deviceId: string, rssi?: number }

export class BleScanner {
  private results: Map<string, Found> = new Map()

  private onFind = (arr: Array<ble.ScanResult>) => {
    arr.forEach(item => {
      const deviceId = item.deviceId
      this.results.set(deviceId, {
        deviceId,
        name: item.deviceName,
        rssi: item.rssi
      })
    })
    console.info(`🔵 found=${this.results.size}`)
  }

  start() {
    // 订阅扫描事件:BLEDeviceFind :contentReference[oaicite:16]{index=16}
    ble.on('BLEDeviceFind', this.onFind)

    // filters 不建议为 null:会增加功耗且扫到非预期设备 :contentReference[oaicite:17]{index=17}
    const filters: Array<ble.ScanFilter> = [
      // 示例:按服务 UUID 或设备名过滤(按你业务填)
      { serviceUuid: '0000180F-0000-1000-8000-00805F9B34FB' } // 电量服务举例
    ]
    const options: ble.ScanOptions = {
      // 具体字段看版本能力,示例表示“别太激进”
    }

    ble.startBLEScan(filters, options) // startBLEScan :contentReference[oaicite:18]{index=18}
  }

  stop() {
    ble.stopBLEScan() // stopBLEScan :contentReference[oaicite:19]{index=19}
    ble.off('BLEDeviceFind', this.onFind)
  }

  list(): Found[] {
    return Array.from(this.results.values()).sort((a, b) => (b.rssi ?? -999) - (a.rssi ?? -999))
  }
}

📶20.4 Wi-Fi 调用——能读就读,别“手欠”一直扫🤣

Wi-Fi 这块有个很现实的版本差异:

  • 老模块 @ohos.wifi 从 API 9 起不再维护,官方建议用 @ohos.wifiManager 等接口
  • wifiManager 的能力列表里包含:isWifiActivegetLinkedInfo、各种事件监听等

✅场景一:判断 Wi-Fi 开关 + 获取当前连接信息

wifiManager.isWifiActive() 用于判断开关状态,需要权限 ohos.permission.GET_WIFI_INFO(这权限名你可别写错😅)

import { wifiManager } from '@kit.ConnectivityKit'
import { promptAction } from '@kit.ArkUI'

async function showWifiStatus() {
  const active = wifiManager.isWifiActive() // :contentReference[oaicite:23]{index=23}
  if (!active) {
    promptAction.showToast({ message: '📶 Wi-Fi 还没开呢,先开开再聊~' })
    return
  }

  const info = await wifiManager.getLinkedInfo() // getLinkedInfo :contentReference[oaicite:24]{index=24}
  console.info(`ssid=${info.ssid}, bssid=${info.bssid}, rssi=${info.rssi}`)
}

✅场景二:监听 Wi-Fi 状态变化(别一直轮询,太“烦人”😅)

wifiManager.on/off 支持监听 wifiStateChange、wifiConnectionChange 等事件

import { wifiManager } from '@kit.ConnectivityKit'

const onState = (data: any) => {
  console.info(`📶 wifiStateChange: ${JSON.stringify(data)}`)
}

function startListenWifi() {
  wifiManager.on('wifiStateChange', onState) // :contentReference[oaicite:26]{index=26}
}

function stopListenWifi() {
  wifiManager.off('wifiStateChange', onState) // :contentReference[oaicite:27]{index=27}
}

🧯20.5 常见翻车点(全是血泪😭,你别笑)

  1. 😵 传感器忘了 off
    你以为只是监听,实际是在持续耗电 + 持续回调,后台一跑就“温热小火炉”🪫
    → 订阅/取消必须成对:on ↔ off

  2. 📍 定位不先判断开关
    官方明确有 isLocationEnabled(),先看开关再干活,用户体验会好很多

  3. 🧠 定位一上来就 getCurrentLocation
    省电第一步:先拿 getLastLocation() 缓存,不够新鲜再实时

  4. 🔵 BLE 扫描 filters 传 null
    官方提醒:不建议这么干,功耗和误扫都会更糟

  5. 📶 Wi-Fi 还用旧 @ohos.wifi 不看弃用说明
    API 9 起不维护了,别等线上出问题才想起升级😭

🧧福利赠与你🧧

  无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」专栏(全网一个名),bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。

  最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。

  同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。

✨️ Who am I?

我是bug菌(全网一个名),CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云多年度十佳博主/价值贡献奖,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;更多精彩福利点击这里;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿。

-End-

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。