鸿蒙 Location Kit(位置服务)

发布于:2025-05-20 ⋅ 阅读:(15) ⋅ 点赞:(0)

移动终端设备已经深入人们日常生活的方方面面,如查看所在城市的天气、新闻轶事、出行打车、旅行导航、运动记录。这些习以为常的活动,都离不开定位用户终端设备的位置。

Location Kit 使用多种定位技术提供服务,可以准确地确定设备在室外/室内的位置:

  • 坐标
    系统以 1984 年世界大地坐标系统为参考,使用经度、纬度、海拔高度数据描述地球上的一个位置。
  • GNSS 定位
    全球导航卫星系统,包含:GPS、GLONASS、北斗、Galileo 等,通过导航卫星、设备芯片提供的定位算法,来确定设备准确位置。定位过程具体使用哪些定位系统,取决于用户设备的硬件能力。
  • 基站定位
    根据设备当前驻网基站和相邻基站的位置,估算设备当前位置。此定位方式的定位结果精度相对较低,并且需要设备可以访问蜂窝网络。
  • WLAN、蓝牙定位
    根据设备可搜索到的周围 WLAN、蓝牙设备位置,估算设备当前位置。此定位方式的定位结果精度依赖设备周围可见的固定 WLAN、蓝牙设备的分布,密度较高时,精度也相较于基站定位方式更高,同时也需要设备可以访问网络

申请定位权限

应用在使用 Location Kit 系统能力前,需要检查是否已经获取用户授权访问设备位置信息。如未获得授权,可以向用户申请需要的位置权限。系统提供的定位相关权限有:

  • ohos.permission.LOCATION:用于获取精准位置,精准度在米级别
  • ohos.permission.APPROXIMATELY_LOCATION:用于获取模糊位置,精确度为 5 公里
  • ohos.permission.LOCATION_IN_BACKGROUND:用于应用切换到后台仍然需要获取定位信息的场景

当 APP 运行在前台,访问设备位置信息时,申请位置权限的方式有两种:
获取模糊位置: ohos.permission.APPROXIMATELY_LOCATION
获取精确位置: ohos.permission.APPROXIMATELY_LOCATION + ohos.permission.LOCATION

当 APP 运行在后台时,除了上述两组权限外,还需要申请如下权限:
后台定位权限: ohos.permission.LOCATION_IN_BACKGROUND
或者申请定位类型的长时任务:backgroundModes: "location"

申请用户权限

import {
  abilityAccessCtrl,
  bundleManager,
  common,
  Permissions,
} from "@kit.AbilityKit";

let permissions: Permissions[] = [
  "ohos.permission.APPROXIMATELY_LOCATION",
  "ohos.permission.LOCATION",
];

let accessMgr = abilityAccessCtrl.createAtManager();
const flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION;
const bundleInfo = await bundleManager.getBundleInfoForSelf(flags);
const grantStatus0 = await accessMgr.checkAccessToken(
  bundleInfo.appInfo.accessTokenId,
  permissions[0]
);
const grantStatus1 = await accessMgr.checkAccessToken(
  bundleInfo.appInfo.accessTokenId,
  permissions[1]
);

if (
  grantStatus0 === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED &&
  grantStatus1 === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED
) {
  let results = await accessMgr.requestPermissionsFromUser(
    getContext(),
    permissions
  );
  if (results.authResults[0] == 0 && results.authResults[1] == 0) {
    // 通过授权
  } else {
    // 拒绝授权
  }
}

相关接口

接口名 功能描述
on(type: 'locationChange', request: LocationRequest | ContinuousLocationRequest, callback: Callback<Location>): void 开启位置变化订阅,并发起定位请求,持续性定位
off(type: 'locationChange', callback?: Callback<Location>): void 关闭位置变化订阅,并删除对应的定位请求,如果不关闭就会产生内存泄漏
getCurrentLocation(request: CurrentLocationRequest | SingleLocationRequest, callback: AsyncCallback<Location>): void 获取当前位置,使用 callback 回调异步返回结果,一次性定位
getCurrentLocation(request?: CurrentLocationRequest | SingleLocationRequest): Promise<Location> 获取当前位置,使用 Promise 方式异步返回结果
getLastLocation(): Location 获取最近一次定位结果
isLocationEnabled(): boolean 判断位置服务是否已经开启

示例

{
  "module":{
    "extensionAbilities":[
       "requestPermissions": [
      //当前应用申请数据和功能的访问权限
      //系统授予级权限 —— 只需要声明name即可
      {
        "name": "ohos.permission.INTERNET"    //互联网访问权限
      },
      //用户授予级权限 —— 必须声明name/reason/usedScene三个属性
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",   //模糊定位
        "reason": "$string:Location_Reason",
        "usedScene": {
          "abilities": ["EntryAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.LOCATION",  //精确定位
        "reason": "$string:Location_Reason", //当前应用需要向用户解释使用该权限的原因
        "usedScene": {                       //权限在何种场景下被使用
          "abilities": ["EntryAbility"],    //哪些Ability/窗口中需要使用该权限
          "when": "always"  //何时使用该权限  inuse:当前应用在前台运行时需要使用   always:总是需要该权限,即使应用没在运行
        }
      }
    ]
    ]
  }
}
import { abilityAccessCtrl, bundleManager, Permissions } from '@kit.AbilityKit'
import { geoLocationManager } from '@kit.LocationKit'
import { JSON } from '@kit.ArkTS'
import { router } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  //页面显式时,先弹出“申请定位权限”授权窗口
  async onPageShow() {
    //① 声明需要用户授权的权限列表
    let list: Permissions[] = ['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION']

    //② 获得当前应用的“访问令牌”
    let flags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION //需要获得整个应用的信息,而不是模块的/应用组件的
    let bundleInfo = await bundleManager.getBundleInfoForSelf(flags) //得到当前资源包信息
    let tokenId = bundleInfo.appInfo.accessTokenId //当前应用的当前分身在当前用户使用场景下,系统分配的令牌编号

    let atManager = abilityAccessCtrl.createAtManager() //At: Access Token,访问令牌,即当前应用的授权列表
    let grantStatus0 = await atManager.checkAccessToken(tokenId, list[0])
    let grantStatus1 = await atManager.checkAccessToken(tokenId, list[1])

    //③ 从访问令牌中查询,用户是否授予过定位权限
    if (grantStatus0 == -1 && grantStatus1 == -1) { //0表示已经通过授权了  -1表尚未尚未授权/之前拒绝授权了
      //④ 如果尚未授权过,则弹出申请授权对话框
      let result = await atManager.requestPermissionsFromUser(getContext(), list)
      if (result.authResults[0] == 0) {
        console.log('1.模糊定位权限已经从用户处申请到')
      } else {
        console.log('2.用户拒绝授予模糊定位权限')
      }
      if (result.authResults[1] == 0) {
        console.log('3.精确定位权限已经从用户处申请到')
      } else {
        console.log('4.用户拒绝授予精确定位权限')
      }
    }
  }

  //当页面隐藏时,取消“持续性定位改变监听”
  onPageHide() {
    try {
      geoLocationManager.off('locationChange')
      console.log('--持续性定位改变监听已经关闭')
    } catch (err) {
      console.log('--关闭持续性定位改变监听失败:', JSON.stringify(err))
    }
  }

  build() {
    Column({ space: 10 }) {
      Text('首页')
        .fontSize(30)

      Button('1.获取用户当前的定位信息——一次性定位').onClick(async _ => {
        if (geoLocationManager.isLocationEnabled()) {
          console.log('--当前系统已打开定位开关,正在获取位置信息....')
          let loc = await geoLocationManager.getCurrentLocation()
          console.log('--成功获取到当前定位信息:', JSON.stringify(loc))

          if (geoLocationManager.isGeocoderAvailable()) {
            let address = await geoLocationManager.getAddressesFromLocation(loc)
            console.log('--当前系统可以进行地理<=>坐标转化',address[0].placeName)
          } else {
            console.log('--当前系统无法进行地理<=>坐标转化')
          }
        } else {
          console.log('--当前系统未启用定位服务,请用户打开定位开关!')
        }
      })

      Button('2.获取用户当前的定位信息——持续性定位').onClick(_ => {
        if (!geoLocationManager.isLocationEnabled()) {
          console.log('--当前系统没有打开定位开关!')
          return
        }
        let count = 0
        geoLocationManager.on('locationChange', {}, (loc) => {
          count++
          console.log('--当前设备位置改变了:', count, JSON.stringify(loc))
        })
      })

      Button('3.跳转到下一个页面,测试是否仍然监听定位改变').onClick(_ => {
        router.pushUrl({
          url: 'pages/Page1'
        })
      })
    }
    .height('100%')
    .width('100%')
    .padding(10)
  }
}

地理编码

使用坐标描述一个位置,非常准确,但是并不直观,面向用户表达并不友好。系统向开发者提供了以下两种转化能力:

  • 地理编码转化:将地理描述转化为具体坐标。
  • 逆地理编码转化:将坐标转化为地理描述。

其中地理编码包含多个属性来描述位置,包括国家、行政区划、街道、门牌号、地址描述等等,这样的信息更便于用户理解

import { geoLocationManager } from '@kit.LocationKit'

try {
    if( geoLocationManager.isGeocoderAvailable() ){ //查询地理编码与逆地理编码服务是否可用
      let loc = {"latitude": 31.12, "longitude": 121.11, ... }
      let addr = await geoLocationManager.getAddressesFromLocation( loc ) //逆地理编码转化
    }
} catch (err) {
    console.error("逆地理编码转化失败:" + JSON.stringify(err));
}


网站公告

今日签到

点亮在社区的每一天
去签到