【鸿蒙开发】第四十五章 Location Kit(位置服务)

目录

1 Location Kit简介

1.1 约束与限制

2 申请位置权限

2.1 开发步骤

3 获取设备的位置信息 

3.1 场景概述

3.2 接口说明

3.3 开发步骤

4 地理编码转化与逆地理编码转化

4.1 场景概述

4.2 接口说明

4.3 开发步骤

5 基于设备自身定位的地理围栏

5.1 场景概述

5.2 接口说明

5.3 开发步骤

6 FenceExtensionAbility

6.1 概述

6.2 使用场景

6.3 接口介绍

6.4 开发步骤

实现一个地理围栏扩展服务


1 Location Kit简介

位置子系统使用多种定位技术提供服务,如GNSS定位、基站定位、WLAN/蓝牙定位(基站定位、WLAN/蓝牙定位后续统称“网络定位技术”);通过这些定位技术,无论用户设备在室内或是户外,都可以准确地确定设备位置。

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

  • 坐标

    系统以1984年世界大地坐标系统为参考,使用经度、纬度数据描述地球上的一个位置。

  • GNSS定位

    基于全球导航卫星系统,包含:GPS、GLONASS、北斗、Galileo等,通过导航卫星、设备芯片提供的定位算法,来确定设备准确位置。定位过程具体使用哪些定位系统,取决于用户设备的硬件能力。

  • 基站定位

    根据设备当前驻网基站和相邻基站的位置,估算设备当前位置。此定位方式的定位结果精度相对较低,并且需要设备可以访问蜂窝网络。

  • WLAN、蓝牙定位

    根据设备可搜索到的周围WLAN、蓝牙设备位置,估算设备当前位置。此定位方式的定位结果精度依赖设备周围可见的固定WLAN、蓝牙设备的分布,密度较高时,精度也相较于基站定位方式更高,同时也需要设备可以访问网络。

1.1 约束与限制

位置能力作为系统为应用提供的一种基础服务,需要应用在所使用的业务场景,向系统主动发起请求,并在业务场景结束时,主动结束此请求,在此过程中系统会将实时的定位结果上报给应用。

使用设备的位置能力,需要用户进行确认并主动开启位置开关。如果位置开关没有开启,系统不会向任何应用提供定位服务

设备位置信息属于用户敏感数据,所以即使用户已经开启位置开关,应用在获取设备位置前仍需向用户申请位置访问权限。在用户确认允许后,系统才会向应用提供定位服务。

2 申请位置权限

系统提供的定位权限有:

  • ohos.permission.LOCATION:用于获取精准位置,精准度在米级别。

  • ohos.permission.APPROXIMATELY_LOCATION:用于获取模糊位置,精确度为5公里。

  • ohos.permission.LOCATION_IN_BACKGROUND:用于应用切换到后台仍然需要获取定位信息的场景。

2.1 开发步骤

  1. 开发者可以在应用配置文件中声明所需要的权限并向用户申请授权

  2. 当APP运行在前台,且访问设备位置信息时,申请位置权限的方式如下:

申请位置权限的方式是否允许申请申请成功后获取的位置的精确度
申请ohos.permission.APPROXIMATELY_LOCATION获取到模糊位置,精确度为5公里。
同时申请ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION获取到精准位置,精准度在米级别。
  1. 当APP运行在后台时,申请位置权限的方式如下:

如果应用在后台运行时也需要访问设备位置,除了按照步骤2申请权限外,还需要申请LOCATION类型的长时任务

3 获取设备的位置信息 

3.1 场景概述

开发者可以调用HarmonyOS位置相关接口,获取设备实时位置,或者最近的历史位置,以及监听设备的位置变化

3.2 接口说明

获取设备的位置信息所使用的接口如下,详细说明参见:Location Kit

本模块能力仅支持WGS-84坐标系。

接口名功能描述
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判断位置服务是否已经开启。

3.3 开发步骤

        1. 获取设备的位置信息,需要有位置权限,位置权限申请的方法和步骤见申请位置权限开发指导

        2. 导入geoLocationManager模块,所有与基础定位能力相关的功能API,都是通过该模块提供的。

import { geoLocationManager } from '@kit.LocationKit';

        3. 调用获取位置接口之前需要先判断位置开关是否打开

        查询当前位置开关状态,返回结果为布尔值,true代表位置开关开启false代表位置开关关闭,示例代码如下:

import { geoLocationManager } from '@kit.LocationKit';
try {
    let locationEnabled = geoLocationManager.isLocationEnabled();
} catch (err) {
    console.error("errCode:" + err.code + ", message:"  + err.message);
}

         如果位置开关未开启,可以拉起全局开关设置弹框,引导用户打开位置开关,具体可参考拉起全局开关设置弹框

        4. 单次获取当前设备位置。多用于查看当前位置、签到打卡、服务推荐等场景。

  • 方式一:获取系统缓存的最新位置。

    如果系统当前没有缓存位置会返回错误码。

    推荐优先使用该接口获取位置,可以减少系统功耗。

    如果对位置的新鲜度比较敏感,可以先获取缓存位置,将位置中的时间戳与当前时间对比,若新鲜度不满足预期可以使用方式二获取位置。

import { geoLocationManager } from '@kit.LocationKit';
import { BusinessError } from '@kit.BasicServicesKit'
try {
    let location = geoLocationManager.getLastLocation();
} catch (err) {
    console.error("errCode:" + JSON.stringify(err));
}

  

方式二:获取当前位置。

首先要实例化SingleLocationRequest对象,用于告知系统该向应用提供何种类型的位置服务,以及单次定位超时时间。

  • 设置LocatingPriority:

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

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

    两种定位策略均会同时使用GNSS定位和网络定位技术,以便在室内和户外场景下均可以获取到位置结果,对设备的硬件资源消耗较大,功耗也较大。

  • 设置locatingTimeoutMs:

    因为设备环境、设备所处状态、系统功耗管控策略等的影响,定位返回的时延会有较大波动,建议把单次定位超时时间设置为10秒。

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

import { geoLocationManager } from '@kit.LocationKit';
import { BusinessError } from '@kit.BasicServicesKit'
let request: geoLocationManager.SingleLocationRequest = {
   'locatingPriority': geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED,
   'locatingTimeoutMs': 10000
}
try {
   geoLocationManager.getCurrentLocation(request).then((result) => { // 调用getCurrentLocation获取当前设备位置,通过promise接收上报的位置
      console.log('current location: ' + JSON.stringify(result));
   })
   .catch((error:BusinessError) => { // 接收上报的错误码
      console.error('promise, getCurrentLocation: error=' + JSON.stringify(error));
   });
 } catch (err) {
   console.error("errCode:" + JSON.stringify(err));
 }

    通过本模块获取到的坐标均为WGS-84坐标系坐标点,如需使用其它坐标系类型的坐标点,请进行坐标系转换后再使用。

        5. 持续定位。多用于导航、运动轨迹、出行等场景。

首先要实例化ContinuousLocationRequest对象,用于告知系统该向应用提供何种类型的位置服务,以及位置结果上报的频率。

  • 设置locationScenario:

    建议locationScenario参数优先根据应用的使用场景进行设置,该参数枚举值定义参见UserActivityScenario,例如地图在导航时使用NAVIGATION参数,可以持续在室内和室外场景获取位置用于导航。

  • 设置interval:

    表示上报位置信息的时间间隔,单位是秒,默认值为1秒。如果对位置上报时间间隔无特殊要求,可以不填写该字段。

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

import { geoLocationManager } from '@kit.LocationKit';
let request: geoLocationManager.ContinuousLocationRequest= {
   'interval': 1,
   'locationScenario': geoLocationManager.UserActivityScenario.NAVIGATION
}
let locationCallback = (location:geoLocationManager.Location):void => {
   console.log('locationCallback: data: ' + JSON.stringify(location));
};
try {
   geoLocationManager.on('locationChange', request, locationCallback);
} catch (err) {
   console.error("errCode:" + JSON.stringify(err));
}

如果不主动结束定位可能导致设备功耗高,耗电快;建议在不需要获取定位信息时及时结束定位。

geoLocationManager.off('locationChange', locationCallback);

4 地理编码转化与逆地理编码转化

4.1 场景概述

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

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

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

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

4.2 接口说明

进行坐标和地理编码信息的相互转化,所使用的接口说明如下,详细信息参见:Location Kit

接口名功能描述
isGeocoderAvailable(): boolean;判断地理编码与逆地理编码服务是否可用。
getAddressesFromLocation(request: ReverseGeoCodeRequest, callback: AsyncCallback<Array<GeoAddress>>): void调用逆地理编码服务,将坐标转换为地理描述,使用callback回调异步返回结果。
getAddressesFromLocationName(request: GeoCodeRequest, callback: AsyncCallback<Array<GeoAddress>>): void调用地理编码服务,将地理描述转换为具体坐标,使用callback回调异步返回结果。

4.3 开发步骤

说明

地理编码与逆地理编码功能需要访问后端服务,请确保设备联网,以进行信息获取。

        1. 导入geoLocationManager模块,所有与地理编码转化&逆地理编码转化能力相关的功能API,都是通过该模块提供的。

import { geoLocationManager } from '@kit.LocationKit';

        2. 查询地理编码与逆地理编码服务是否可用。

  • 调用isGeoServiceAvailable查询地理编码与逆地理编码服务是否可用,如果服务可用再继续进行步骤3。如果服务不可用,说明该设备不具备地理编码与逆地理编码能力,请勿使用相关接口。

import { geoLocationManager } from '@kit.LocationKit';
try {
    let isAvailable = geoLocationManager.isGeocoderAvailable();
} catch (err) {
    console.error("errCode:" + JSON.stringify(err));
}

         3. 获取转化结果。

  • 调用getAddressesFromLocation,把坐标转化为地理位置信息。应用可以获得与此坐标匹配的GeoAddress(地理编码地址信息)列表,应用可以根据实际使用需求,读取相应的参数数据。

let reverseGeocodeRequest:geoLocationManager.ReverseGeoCodeRequest = {"latitude": 31.12, "longitude": 121.11, "maxItems": 1};
try {
    geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, (err, data) => {
        if (err) {
            console.log('getAddressesFromLocation err: ' + JSON.stringify(err));
        } else {
            console.log('getAddressesFromLocation data: ' + JSON.stringify(data));
        }
    });
} catch (err) {
    console.error("errCode:" + JSON.stringify(err));
}
  •  调用getAddressesFromLocationName把位置描述转化为坐标。
let geocodeRequest:geoLocationManager.GeoCodeRequest = {"description": "上海市浦东新区xx路xx号", "maxItems": 1};
try {
    geoLocationManager.getAddressesFromLocationName(geocodeRequest, (err, data) => {
        if (err) {
            console.log('getAddressesFromLocationName err: ' + JSON.stringify(err));
        } else {
            console.log('getAddressesFromLocationName data: ' + JSON.stringify(data));
        }
    });
} catch (err) {
    console.error("errCode:" + JSON.stringify(err));
}

应用可以获得与位置描述相匹配的GeoAddress(地理编码地址信息)列表,其中包含对应的坐标数据。

如果需要查询的位置描述可能出现多地重名的请求,可以设置GeoCodeRequest,通过设置一个经纬度范围,以高效地获取期望的准确结果。

5 基于设备自身定位的地理围栏

5.1 场景概述

地理围栏就是虚拟地理边界,当设备进入、离开某个特定地理区域时,可以接收自动通知和警告

目前仅支持圆形围栏,并且依赖GNSS芯片的地理围栏功能,仅在室外开阔区域才能准确识别用户进出围栏事件。

5.2 接口说明

地理围栏所使用的接口如下,详细说明参见:Location Kit

接口名功能描述
on(type: 'gnssFenceStatusChange', request: GeofenceRequest, want: WantAgent): void;添加一个围栏,并订阅地理围栏事件。
off(type: 'gnssFenceStatusChange', request: GeofenceRequest, want: WantAgent): void;删除一个围栏,并取消订阅该围栏事件。

5.3 开发步骤

        1. 使用地理围栏功能,需要有权限ohos.permission.APPROXIMATELY_LOCATION,位置权限申请的方法和步骤见申请位置权限开发指导

        2. 导入geoLocationManager模块、wantAgent模块和BusinessError模块。

import { geoLocationManager } from '@kit.LocationKit';
import { wantAgent } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit'

        3. 创建WantAgentInfo信息。

        场景一:创建拉起Ability的WantAgentInfo信息。

// 通过WantAgentInfo的operationType设置动作类型
let wantAgentInfo:wantAgent.WantAgentInfo = {
    wants: [
        {
            deviceId: '',
            bundleName: 'com.example.myapplication',
            abilityName: 'EntryAbility',
            action: '',
            entities: [],
            uri: '',
            parameters: {}
        }
    ],
    operationType: wantAgent.OperationType.START_ABILITY,
    requestCode: 0,
    wantAgentFlags:[wantAgent.WantAgentFlags.CONSTANT_FLAG]
};

        场景二:创建发布公共事件的WantAgentInfo信息。

// 通过WantAgentInfo的operationType设置动作类型
let wantAgentInfo:wantAgent.WantAgentInfo = {
    wants: [
        {
            action: 'event_name', // 设置事件名
            parameters: {},
        }
    ],
    operationType: wantAgent.OperationType.SEND_COMMON_EVENT,
    requestCode: 0,
    wantAgentFlags: [wantAgent.WantAgentFlags.CONSTANT_FLAG],
}

        4. 调用getWantAgent()方法进行创建WantAgent

        并且在获取到WantAgent对象之后调用地理围栏接口添加围栏,当设备进入或者退出该围栏时,系统会自动触发WantAgent的动作。

let wantAgentObj : object | undefined = undefined;
// 创建WantAgent
wantAgent.getWantAgent(wantAgentInfo, (err, data) => {
    if (err) {
      console.error('getWantAgent err=' + JSON.stringify(err));
      return;
    }
    console.info('getWantAgent success');
    wantAgentObj = data;
    let requestInfo:geoLocationManager.GeofenceRequest = {'scenario': 0x301, "geofence": {"latitude": 31.12, "longitude": 121.11, "radius": 100, "expiration": 10000}};
    try {
        geoLocationManager.on('gnssFenceStatusChange', requestInfo, wantAgentObj);
    } catch (err) {
        console.error("errCode:" + JSON.stringify(err));
    }
});

6 FenceExtensionAbility

6.1 概述

FenceExtensionAbility是一个地理围栏类型ExtensionAbility,可以使用该Ability来快速便捷的实现地理围栏拓展能力,协助用户完成快捷交互,提升用户体验。

6.2 使用场景

  1. 通过位置服务的geoLocationManager.on('gnssFenceStatusChange')接口进行地理围栏的注册和事件订阅,并在want参数中传入实现的FenceExtensionAbility相关参数信息;
  2. 系统会在注册的地理围栏触发后,通过onFenceStatusChange接口上报围栏事件和数据,APP可以在接收到事件后进行相应的业务处理,例如发送一个通知。

6.3 接口介绍

接口详情参见FenceExtensionAbility

接口描述
onFenceStatusChange(transition: geoLocationManager.GeofenceTransition, additions: Record<string, string>): void接收系统通知的地理围栏事件,根据围栏事件类型和数据进行相应处理。
onDestroy(): void接收FenceExtensionAbility的销毁事件并处理,会在FenceExtensionAbility销毁前回调。

6.4 开发步骤

实现一个地理围栏扩展服务

首先需要实现FenceExtensionAbility提供方的能力。

需要在DevEco Studio工程中手动新建一个FenceExtensionAbility,具体步骤如下。

  1. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为fenceextensionability;
  2. 在fenceextensionability目录,右键选择“New > File”,新建一个.ets文件并命名为MyFenceExtensionAbility.ets;
  3. 打开MyFenceExtensionAbility.ets,导入FenceExtensionAbility的依赖包,自定义类继承FenceExtensionAbility并实现onFenceStatusChange和onDestroy接口;

示例代码如下:

import { FenceExtensionAbility, geoLocationManager } from '@kit.LocationKit';
import { notificationManager } from '@kit.NotificationKit';
import { Want, wantAgent } from '@kit.AbilityKit';

export class MyFenceExtensionAbility extends FenceExtensionAbility {
  onFenceStatusChange(transition: geoLocationManager.GeofenceTransition, additions: Record<string, string>): void {
    // 接受围栏状态变化事件,处理业务逻辑
    console.info(`on geofence transition,id:${transition.geofenceId},event:${transition.transitionEvent},additions:${JSON.stringify(additions)}`);

    // 可以发送围栏业务通知
    let wantAgentInfo: wantAgent.WantAgentInfo = {
      wants: [
        {
          bundleName: 'com.example.myapplication',
          abilityName: 'EntryAbility',
          parameters:
          {
            "geofenceId": transition?.geofenceId,
            "transitionEvent": transition?.transitionEvent,
          }
        } as Want
      ],
      actionType: wantAgent.OperationType.START_ABILITY,
      requestCode: 100
    };
    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentMy) => {
      let notificationRequest: notificationManager.NotificationRequest = {
        id: 1,
        content: {
          notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
          normal: {
            title: `围栏通知`,
            text: `on geofence transition,id:${transition.geofenceId},event:${transition.transitionEvent},additions:${JSON.stringify(additions)}`,
          }
        },
        notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION,
        wantAgent: wantAgentMy
      };
      notificationManager.publish(notificationRequest);
    });
  }
}

        1. 在工程Module对应的module.json5配置文件中注册FenceExtensionAbility,type标签需要设置为fencesrcEntry标签表示当前FenceExtensionAbility组件所对应的代码路径

{
  "module": {
    "extensionAbilities": [
      {
        "name": "MyFenceExtensionAbility",
        "srcEntry": "./ets/fenceExtensionability/MyFenceExtensionAbility.ets",
        "description": "MyFenceExtensionAbility",
        "type": "fence",
        "exported": true
      },
    ]
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值