概述
OpenHarmony位置服务(Location Kit)是一套完整的定位服务解决方案,为开发者提供多种定位能力和位置相关功能。Location Kit支持多种定位技术,包括GNSS(全球导航卫星系统)、基站定位、WLAN定位和蓝牙定位等,能够满足不同应用场景下的定位需求。
位置服务提供的主要功能包括:
- 设备位置获取(单次定位和连续定位)
- 地理围栏管理
- 地理编码与逆地理编码转换
- 位置权限管理
- 云侧地理围栏扩展能力
系统架构
Location Kit采用分层架构设计,主要包括以下层次:
- 应用层:提供ArkTS和C/C++两种开发接口,满足不同开发需求
- 服务层:负责位置服务的核心功能实现,包括定位算法、地理围栏管理等
- 硬件抽象层:适配不同的定位硬件和传感器
- 数据源层:整合多种定位数据源,包括GNSS、基站、WLAN和蓝牙等
核心功能模块
1. 位置获取
Location Kit支持两种位置获取方式:
1.1 单次定位
通过getCurrentLocation接口获取当前位置信息,适用于需要即时获取位置的场景,如签到打卡、拍照记录位置等。
1.2 连续定位
通过on('locationChange')接口订阅位置变化事件,适用于需要持续跟踪位置变化的场景,如导航、轨迹记录等。
2. 地理围栏
地理围栏功能允许开发者定义虚拟地理边界,当设备进入、离开或停留在这些区域时触发相应事件。
2.1 设备侧地理围栏
基于设备自身定位能力实现的地理围栏,支持圆形围栏,主要适用于室外GNSS环境下的应用场景,如广告推送、安全区域监控等。
2.2 云侧地理围栏
通过FenceExtensionAbility实现的云侧地理围栏扩展能力,支持更复杂的围栏逻辑和云端协同处理。
3. 地理编码与逆地理编码
3.1 地理编码
将地理描述(如地址、地标名称)转换为坐标信息,通过getAddressesFromLocationName接口实现。
3.2 逆地理编码
将坐标信息转换为地理描述,通过getAddressesFromLocation接口实现。
4. 位置权限管理
Location Kit提供精细化的位置权限控制:
ohos.permission.LOCATION:获取精准位置,精度在米级别ohos.permission.APPROXIMATELY_LOCATION:获取模糊位置,精确度为5公里ohos.permission.LOCATION_IN_BACKGROUND:应用在后台时仍可获取位置信息
开发接口
ArkTS接口
1. 位置获取
typescript
import { geoLocationManager } from '@kit.LocationKit';
// 检查位置开关状态
async function checkLocationEnabled(): Promise<boolean> {
return await geoLocationManager.isLocationEnabled();
}
// 单次定位
async function getCurrentLocation() {
try {
const requestInfo: geoLocationManager.LocationRequest = {
'priority': geoLocationManager.LocationRequestPriority.ACCURACY,
'timeInterval': 1,
'distanceInterval': 0,
'maxAccuracy': 0
};
const location = await geoLocationManager.getCurrentLocation(requestInfo);
console.log(`当前位置: 纬度${location.latitude}, 经度${location.longitude}`);
return location;
} catch (error) {
console.error('获取位置失败:', error);
}
}
// 订阅位置变化
function subscribeLocationChange() {
const requestInfo: geoLocationManager.LocationRequest = {
'priority': geoLocationManager.LocationRequestPriority.ACCURACY,
'timeInterval': 1,
'distanceInterval': 0,
'maxAccuracy': 0
};
geoLocationManager.on('locationChange', requestInfo, (location) => {
console.log(`位置更新: 纬度${location.latitude}, 经度${location.longitude}`);
});
}
// 取消订阅位置变化
function unsubscribeLocationChange() {
geoLocationManager.off('locationChange');
}
2. 地理围栏
typescript
import { geoLocationManager } from '@kit.LocationKit';
// 添加GNSS地理围栏
async function addGeofence() {
const geofenceRequest: geoLocationManager.GeofenceRequest = {
"priority": geoLocationManager.GeofenceRequestPriority.ACCURACY,
"geofence": {
"latitude": 39.9,
"longitude": 116.4,
"radius": 200,
"expiration": 10000
}
};
const geofenceId = await geoLocationManager.addGnssGeofence(geofenceRequest);
console.log(`地理围栏ID: ${geofenceId}`);
return geofenceId;
}
// 监听地理围栏状态变化
function listenGeofenceStatusChange() {
geoLocationManager.on('gnssFenceStatusChange', (geofence) => {
console.log(`围栏状态变化: ID=${geofence.geofenceId}, 事件=${geofence.transitionEvent}`);
});
}
// 移除地理围栏
async function removeGeofence(geofenceId: number) {
await geoLocationManager.removeGnssGeofence(geofenceId);
console.log(`已移除地理围栏: ${geofenceId}`);
}
3. 地理编码与逆地理编码
typescript
import { geoLocationManager } from '@kit.LocationKit';
// 地理编码:地址转坐标
async function geocode() {
try {
const isAvailable = await geoLocationManager.isGeocoderAvailable();
if (!isAvailable) {
console.error('地理编码服务不可用');
return;
}
const geocodeRequest: geoLocationManager.GeoAddress = {
"description": "北京市海淀区",
"maxItems": 1
};
const addresses = await geoLocationManager.getAddressesFromLocationName(geocodeRequest);
if (addresses.length > 0) {
const address = addresses[0];
console.log(`坐标: 纬度${address.latitude}, 经度${address.longitude}`);
}
} catch (error) {
console.error('地理编码失败:', error);
}
}
// 逆地理编码:坐标转地址
async function reverseGeocode(latitude: number, longitude: number) {
try {
const isAvailable = await geoLocationManager.isGeocoderAvailable();
if (!isAvailable) {
console.error('逆地理编码服务不可用');
return;
}
const reverseGeocodeRequest: geoLocationManager.GeoAddress = {
"latitude": latitude,
"longitude": longitude,
"maxItems": 1
};
const addresses = await geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest);
if (addresses.length > 0) {
const address = addresses[0];
console.log(`地址: ${address.description}`);
}
} catch (error) {
console.error('逆地理编码失败:', error);
}
}
4. FenceExtensionAbility实现
typescript
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(`围栏状态变化: ID=${transition.geofenceId}, 事件=${transition.transitionEvent}`);
// 可以发送围栏业务通知
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: `围栏状态变化: ID=${transition.geofenceId}, 事件=${transition.transitionEvent}`,
}
},
notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION,
wantAgent: wantAgentMy
};
notificationManager.publish(notificationRequest);
});
}
onDestroy(): void {
console.info('FenceExtensionAbility销毁');
}
}
C/C++接口
Location Kit也提供了C/C++接口,适用于需要高性能或底层控制的应用场景。
1. 基本配置
c
#include "LocationKit/oh_location.h"
#include "LocationKit/oh_location_type.h"
#include "hilog/log.h"
#include <stdlib.h>
// 创建位置请求配置
struct Location_RequestConfig *g_requestConfig = NULL;
void *mydata = NULL;
2. 检查位置服务状态
c
static napi_value OhLocationIsEnabled(napi_env env, napi_callback_info info)
{
bool isEnabled = false;
int resultCode = OH_Location_IsLocatingEnabled(&isEnabled);
napi_value result = NULL;
napi_get_boolean(env, isEnabled, &result);
return result;
}
3. 位置订阅与取消订阅
c
// 定义回调函数接收位置信息
void reportLocation(Location_Info* location, void* userData)
{
Location_BasicInfo baseInfo = OH_LocationInfo_GetBasicInfo(location);
char additionalInfo[1024] = "";
Location_ResultCode result = OH_LocationInfo_GetAdditionalInfo(location, additionalInfo, sizeof(additionalInfo));
OH_LOG_INFO(LOG_APP, "位置信息: 纬度=%f, 经度=%f", baseInfo.latitude, baseInfo.longitude);
if (mydata == userData) {
OH_LOG_INFO(LOG_APP, "用户数据匹配");
}
return;
}
// 订阅位置信息
static napi_value OhLocationStartLocating(napi_env env, napi_callback_info info)
{
if (g_requestConfig == NULL) {
g_requestConfig = OH_Location_CreateRequestConfig();
}
OH_LocationRequestConfig_SetUseScene(g_requestConfig, LOCATION_USE_SCENE_NAVIGATION);
OH_LocationRequestConfig_SetInterval(g_requestConfig, 1);
mydata = (void *)malloc(sizeof("mydata")); // 用户自定义任意类型,callback 透传返回
OH_LocationRequestConfig_SetCallback(g_requestConfig, reportLocation, mydata);
OH_Location_StartLocating(g_requestConfig);
int32_t ret = 0;
napi_value result = NULL;
napi_create_int32(env, ret, &result);
return result;
}
// 取消订阅位置信息
static napi_value OhLocationStopLocating(napi_env env, napi_callback_info info)
{
OH_Location_StopLocating(g_requestConfig);
if (g_requestConfig != NULL) {
OH_Location_DestroyRequestConfig(g_requestConfig);
g_requestConfig = NULL;
}
free(mydata);
mydata = NULL;
int32_t ret = 0;
napi_value result = NULL;
napi_create_int32(env, ret, &result);
return result;
}
4. 模块初始化
c
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc[] = {
{"ohLocationIsEnabled", NULL, OhLocationIsEnabled, NULL, NULL, NULL, napi_default, NULL},
{"ohLocationStartLocating", NULL, OhLocationStartLocating, NULL, NULL, NULL, napi_default, NULL},
{"ohLocationStopLocating", NULL, OhLocationStopLocating, NULL, NULL, NULL, napi_default, NULL},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
权限要求
基本权限
使用Location Kit需要在应用配置文件中声明以下权限:
json
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:location_reason",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when": "inuse"
}
}
]
}
}
后台定位权限
如果应用需要在后台获取位置信息,除了基本位置权限外,还需要:
- 申请
ohos.permission.LOCATION_IN_BACKGROUND权限 - 申请LOCATION类型的长时任务
权限申请策略
| 申请位置权限的方式 | 是否允许申请 | 申请成功后获取的位置的精确度 |
|---|---|---|
| 申请ohos.permission.APPROXIMATELY_LOCATION | 是 | 获取到模糊位置,精确度为5公里。 |
| 同时申请ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION | 是 | 获取到精准位置,精准度在米级别。 |
约束与限制
- 位置开关:使用Location Kit前需要确保设备的位置服务开关已开启
- 权限授权:应用必须获得用户授权才能访问位置信息
- 坐标系:Location Kit使用WGS-84坐标系
- 地理围栏限制 :
- 设备侧地理围栏主要适用于室外GNSS环境
- 同一应用最多可创建1000个地理围栏
- 围栏半径范围:1米到100000米
- 后台定位限制:应用在后台使用位置服务需要申请长时任务
总结
OpenHarmony Location Kit提供了全面的位置服务能力,支持多种定位技术和应用场景。开发者可以根据应用需求选择合适的接口和定位策略,实现丰富多样的位置相关功能。在使用Location Kit时,需要特别注意权限管理和资源优化,以提供良好的用户体验并保护用户隐私。