概述
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时,需要特别注意权限管理和资源优化,以提供良好的用户体验并保护用户隐私。