探索HarmonyOS位置服务:精准定位的科技奥秘

精华
2024-09-07 20:27:20
134次阅读
0个评论
最后修改时间:2024-09-07 22:46:21

数字化飞速发展的今天,我们所拥有的智能设备中各种各样的应用程序,其中的位置服务功能正悄然改变着我们的生活方式。比如外卖订餐App可根据我们所在位置推荐周边商家和外卖小哥,短视频App可根据我们所在位置推荐周边用户发布的视频。再比如导航类App精准地确定我们的位置,并为我们规划出最佳的出现路线,无论是日常通勤还是陌生城市的探索,都能让我们轻松找到目的地,避免迷路的困扰。当然在紧急情况下,位置服务还可以为救援人员提供准确的位置信息,缩短救援时间。

HarmonyOS位置服务(Location Kit)使用以下多种定位技术提供服务,为用户提供准确地位置信息。

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

HarmonyOS位置服务(Location Kit)除了提供基础的定位服务之外,还提供了地理围栏、地理编码、逆地理编码、国家码等功能和接口。

应用程序的位置服务,如同一把双刃剑,既为我们的生活带来了前所未有的便利和精彩,也需要我们在享受其好处的同时,注重个人隐私的保护。在 HarmonyOS 中,当应用程序处于业务场景且需要位置服务(Location Kit)时,系统进行了严格的约束与限制。这一举措旨在保护用户的隐私安全,确保位置信息不被滥用。使用设备位置能力时,需要用户进行确认并主动开启位置开关。若位置开关未开启,系统不会向任何应用提供定位服务。由于设备位置属于用户敏感数据,所以即使用户已经开启位置开关,应用在获取设备位置前仍需要向用户申请位置访问权限。在用户确认允许后,系统才会向应用提供定位服务,如下图所示。

module.json5配置文件中声明位置权限

应用程序要想使用位置信息,需要检查是否已经获取用户授权访问设备位置信息,若未获得授权,可以向用户申请需要的位置权限。系统提供的定位权限有:

权限名称 权限级别 授权方式 说明
ohos.permission.LOCATION normal user_grant 允许应用获取设备位置信息。
需要与模糊位置权限一起申请。
ohos.permission.APPROXIMATELY_LOCATION normal user_grant 允许应用获取设备模糊位置信息。
ohos.permission.LOCATION_IN_BACKGROUND normal user_grant 允许应用在后台运行时获取设备位置信息。
由于安全隐私要求,应用不能通过弹窗的形式被授予后台位置权限,应用如果需要使用后台位置权限,需要引导用户到设置界面手动授予。

加入App运行在前台,且访问设备当前的精准位置信息,需要在module.json5中申请如下权限,并向用户申请授权,具体可参考HarmonyOS应用程序访问控制探究

{
  "module": {
    ...
    "requestPermissions": [
      {
        "name": "ohos.permission.LOCATION",	// 权限名称,为系统已定义的权限
        "reason": "$string:location_reason",	// 申请权限的原因,当申请权限为user_grant权限时该字段为必填
        "usedScene": {	// 用于描述权限使用场景,当申请权限为user_grant权限时该字段为必填
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"	// 调用时机(inuse:使用时;always:始终)
        }
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:location_reason",
        "usedScene": {
          "abilities": [
            "EntryAbility"
          ],
          "when": "inuse"
        }
      }
    ]
  }
}

导入geoLocationManager模块

geoLocationManager模块提供GNSS定位、网络定位(蜂窝基站、WLAN、蓝牙定位技术)、地理编码、逆地理编码、国家码和地理围栏等基本功能API。

import { geoLocationManager } from '@kit.LocationKit'

获取当前设备位置

单次获取当前设备位置

多用于查看当前位置、签到打卡、服务推荐等场景。需要实例化SingleLocationRequest对象,用于告知系统该向应用提供何种类型的位置服务,以及单次定位超时时间。

/**
 * 单次定位的请求参数
 */
export interface SingleLocationRequest {
    /**
     * 优先级信息
     */
    locatingPriority: LocatingPriority;
    /**
     * 超时时间,单位是毫秒
     */
    locatingTimeoutMs: number;
}

如果对位置的返回精度要求较高,建议LocatingPriority参数优先选择PRIORITY_ACCURACY,会将一段时间内精度较好的结果返回给应用。

如果对定位速度要求较高,建议LocatingPriority参数优先选择PRIORITY_LOCATING_SPEED,会将最先拿到的定位结果返回给应用。

/**
 * 单次位置请求中的优先级类型
 */
export enum LocatingPriority {
    /**
     * 精度优先。
     * 定位精度优先策略会同时使用GNSS定位和网络定位技术,并将一段时间内精度较好的结果返回给应用。
     * 这个时间段长度为SignleLocationRequest.locationTimeoutMs与“30秒”中的较小者。
     * 对设备的硬件资源消耗较大,功耗较大。
     */
    PRIORITY_ACCURACY = 0x501,
    /**
     * 快速获取位置优先。
     * 快速定位优先策略会同时使用GNSS定位和网络定位技术,以便在室内和户外场景下均可以快速获取到位置结果。
     * 对设备的硬件资源消耗较大,功耗也较大。
     */
    PRIORITY_LOCATING_SPEED = 0x502
}

以快速定位策略(PRIORITY_LOCATING_SPEED)为例,调用方式如下:

/**
 * 单次获取当前位置信息
 * @param priority
 * @param timeout
 * @returns
 */
static async getSingleLocationRequest(priority: geoLocationManager.LocatingPriority,
  timeout: number = 10000): Promise<geoLocationManager.Location | undefined> {
  const request: geoLocationManager.SingleLocationRequest = {
    locatingPriority: priority,
    locatingTimeoutMs: timeout
  };
  let location: geoLocationManager.Location | undefined = undefined;
  try {
    location = await geoLocationManager.getCurrentLocation(request);
    console.info(`[AppLogger]单次获取当前位置信息:${JSON.stringify(location)}`);
  } catch (error) {
    const err = error as BusinessError;
    console.error(`[AppLogger]单次获取当前位置信息异常:${JSON.stringify(err)}`);
  }
  return location;
}


Button('单次获取当前位置信息')
    .onClick(async () => {
      this.location = await LocationUtil.getSingleLocationRequest(geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED);
    })

以上是通过实例化SingleLocationRequest对象来获取当前位置信息,还可以实例化当前位置信息请求参数CurrentLocationRequest来获取当前位置信息,可以根据业务选择使用合适的实例化对象。

/**
 * 当前位置信息请求参数
 */
export interface CurrentLocationRequest {
    /**
     * 位置请求中位置信息优先级类型。
     * 当scenario取值为UNSET时,priority参数生效,否则priority参数不生效。
     * 当scenario和priority均取值为UNSET时,无法发起定位请求。
     */
    priority?: LocationRequestPriority;
    /**
     * 位置请求中定位场景类型。
     * 当scenario取值为UNSET时,priority参数生效,否则priority参数不生效。
     * 当scenario和priority均取值为UNSET时,无法发起定位请求。
     */
    scenario?: LocationRequestScenario;
    /**
     * 精度信息,单位为米。
     * 仅在精确位置功能场景(同时授予了ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION 权限)下有效,模糊位置功能生效场景(仅授予了ohos.permission.APPROXIMATELY_LOCATION 权限)下该字段无意义。
     * 当scenario为NAVIGATION/TRAJECTORY_TRACKING/CAR_HAILING或者priority为ACCURACY时建议设置maxAccuracy为大于10的值。
     * 当scenario为DAILY_LIFE_SERVICE/NO_POWER或者priority为LOW_POWER/FIRST_FIX时建议设置maxAccuracy为大于100的值。
     * 默认值为0,取值范围为大于等于0。
     */
    maxAccuracy?: number;
    /**
     * 超时时间,单位为毫秒。
     */
    timeoutMs?: number;
}


/**
 * 位置请求中位置信息优先级类型
 */
export enum LocationRequestPriority {
    /**
     * 默认优先级,表示未设置优先级,LocationRequestPriority无效。
     */
    UNSET = 0x200,
    /**
     * 精度优先。
     * 定位精度优先策略主要以GNSS定位技术为主,会在GNSS提供稳定位置结果之前使用网络定位技术提供服务。
     * 在持续定位过程中,如果超过30秒无法获取GNSS定位结果则使用网络定位技术。
     */
    ACCURACY,
    /**
     * 低功耗优先。
     * 低功耗定位优先策略仅使用网络定位技术,在室内和户外场景均可提供定位服务。
     * 由于其依赖周边基站、可见WLAN、蓝牙设备的分布情况,定位结果精度波动范围较大,推荐在定位结果精度要求不高的场景下使用。
     */
    LOW_POWER,
    /**
     * 快速获取位置优先。
     * 快速定位优先策略会同时使用GNSS定位和网络技术定位,以便在室内和户外场景下均可以快速获取到位置结果。
     */
    FIRST_FIX
}


/**
 * 位置请求中定位场景类型
 */
export enum LocationRequestScenario {
    /**
     * 默认场景信息,表示未设置场景信息。
     * LocationRequestScenario字段无效。
     */
    UNSET = 0x300,
    /**
     * 导航场景。
     * 适用于在户外获取设备实时位置的场景,如车载、步行导航。
     * 主要是用GNSS定位技术提供定位服务。
     */
    NAVIGATION,
    /**
     * 表示运动轨迹记录场景。
     * 适用于记录用户位置轨迹的场景,如运动类应用记录轨迹功能。
     * 主要使用GNSS定位技术提供定位服务。
     */
    TRAJECTORY_TRACKING,
    /**
     * 打车场景。
     * 适用于用户出行打车时定位当前位置的场景,如网约车类应用。
     * 主要使用GNSS定位技术提供定位服务。
     */
    CAR_HAILING,
    /**
     * 日常服务使用场景。
     * 适用于不需要定位用户精确位置的使用场景,如新闻资讯、网购、点餐类应用。
     * 该场景仅使用网络定位技术提供定位服务。
     */
    DAILY_LIFE_SERVICE,
    /**
     * 无功耗场景。
     * 该场景不会主动触发定位,会在其他应用定位时,才给当前应用返回位置。
     */
    NO_POWER
}

以快速获取位置(FIRST_FIX)策略为例,调用代码如下:

/**
 * 使用 {CurrentLocationRequest} 实例获取位置信息
 * @param priority
 * @param scenario
 * @param maxAccuracy
 * @param timeout
 * @returns 
 */
static async getCurrentLocationRequest(priority: geoLocationManager.LocationRequestPriority,
  scenario: geoLocationManager.LocationRequestScenario, 
  maxAccuracy: number = 0, timeout: number = 10000): Promise<geoLocationManager.Location | undefined> {
  const request: geoLocationManager.CurrentLocationRequest = {
    priority: priority,
    scenario: scenario,
    maxAccuracy: maxAccuracy,
    timeoutMs: timeout
  };
  let location: geoLocationManager.Location | undefined = undefined;
  try {
    location = await geoLocationManager.getCurrentLocation(request);
    console.info(`[AppLogger]单次获取当前位置信息:${JSON.stringify(location)}`);
  } catch (error) {
    const err = error as BusinessError;
    console.error(`[AppLogger]单次获取当前位置信息异常:${JSON.stringify(err)}`);
  }
  return location;
}


Button('使用CurrentLocationRequest实例获取当前位置信息')
  .onClick(async () => {
    this.location = await LocationUtil.getCurrentLocationRequest(geoLocationManager.LocationRequestPriority.FIRST_FIX
      , geoLocationManager.LocationRequestScenario.UNSET);
  })

持续定位

多用于导航、运动轨迹、出行等场景。需要实例化ContinuousLocationRequest对象,用于告知系统该向应用提供何种类型的位置服务,以及位置结果上报的频率。

/**
 * 持续定位的请求参数
 */
export interface ContinuousLocationRequest {
    /**
     * 上报位置信息的时间间隔,单位为秒。
     * 默认为1,取值范围为大于等于0。
     * 等于0时对位置上报时间间隔无限制。
     */
    interval: number;
    /**
     * 定位的场景信息。
     */
    locationScenario: UserActivityScenario | PowerConsumptionScenario;
}

/**
 * 位置请求中的用户活动场景类型
 */
export enum UserActivityScenario {
    /**
     * 导航场景。
     * 适用于在户外获取设备实时位置的场景,如车载、步行导航。
     * 主要使用GNSS定位技术提供定位服务。
     */
    NAVIGATION = 0x401,
    /**
     * 运动场景。
     * 适用于记录用户位置轨迹的场景,如运动类应用记录轨迹功能。
     * 主要使用GNSS定位技术提供定位服务。
     */
    SPORT = 0x402,
    /**
     * 出行场景。
     * 适用于用户出行场景,如打车、乘坐公共交通等场景。
     * 主要使用GNSS定位技术提供定位服务。
     */
    TRANSPORT = 0x403,
    /**
     * 日常服务使用场景。
     * 适用于不需要定位用户精确位置的使用场景,如新闻资讯、网购、点餐类应用。
     * 该场景仅使用网络定位技术提供定位服务。
     */
    DAILY_LIFE_SERVICE = 0x404
}

/**
 * 位置请求中的功耗场景类型
 */
export enum PowerConsumptionScenario {
    /**
     * 高功耗。
     * 以GNSS定位技术为主。
     */
    HIGH_POWER_CONSUMPTION = 0x601,
    /**
     * 低功耗。
     * 适用于对用户位置精度要求不高的使用场景,如新闻资讯、网购、点餐类应用。
     */
    LOW_POWER_CONSUMPTION = 0x602,
    /**
     * 无功耗。
     * 该场景下不会主动触发定位,会在其他应用定位时,才给当前应用返回位置。
     */
    NO_POWER_CONSUMPTION = 0x603
}

以地图导航场景为例,调用方式如下:

/**
 * 持续定位
 * @param interval
 * @param scenario
 * @returns
 */
static async getContinuousLocationRequest(interval: number = 0,
  scenario: geoLocationManager.UserActivityScenario): Promise<geoLocationManager.Location | undefined> {
  const request: geoLocationManager.ContinuousLocationRequest = {
    interval,
    locationScenario: scenario
  };
  let locationPromise: Promise<geoLocationManager.Location | undefined> = new Promise(resolve => {
    let listener: (data: geoLocationManager.Location) => void;
    listener = (data: geoLocationManager.Location) => {
      console.info(`[AppLogger]持续定位数据:${JSON.stringify(data)}`);
      resolve(data);
    }
    try {
      // 开启位置变化订阅
      geoLocationManager.on('locationChange', request, listener);
    } catch (error) {
      const err = error as BusinessError;
      console.error(`[AppLogger]持续定位异常:${JSON.stringify(err)}`);
    }
  });
  return locationPromise;
}


Button('持续定位')
  .onClick(async () => {
    this.location = await LocationUtil.getContinuousLocationRequest(1, geoLocationManager.UserActivityScenario.NAVIGATION);
  })

当然,最后还需要主动结束定位,不然会导致设备功耗高,耗电快,发热等问题。

geoLocationManager.off('locationChange');

总结

位置服务(Location Kit)是应用程序常见的能力之一,如天气预报App中可以使用位置服务查看所在城市的天气、健康运动类App中可以使用位置服务记录运动轨迹等。当开发者在一个你哟功能程序中使用位置服务时,需要按照约束与限制,确保用户敏感数据的安全。

关于作者 白晓明 宁夏图尔科技有限公司董事长兼CEO、坚果派联合创始人 华为HDE、润和软件HiHope社区专家、鸿蒙KOL、仓颉KOL 华为开发者学堂/51CTO学堂/CSDN学堂认证讲师 开放原子开源基金会2023开源贡献之星 公众号:开源开发者新视界(openwatcher)

收藏11

登录 后评论。没有帐号? 注册 一个。