当前位置: 首页 > ds >正文

HarmonyOS 获取设备位置信息开发指导

HarmonyOS 获取设备位置信息开发指导

场景概述

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

对于位置敏感的应用业务,建议获取设备实时位置信息。如果不需要设备实时位置信息,并且希望尽可能的节省耗电,开发者可以考虑获取最近的历史位置。

接口说明

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

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

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

开发步骤

1. 权限申请

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

2. 导入模块

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

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

3. 检查位置开关状态

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

查询当前位置开关状态,返回结果为布尔值,true代表位置开关开启,false代表位置开关关闭。

import { geoLocationManager } from '@kit.LocationKit';
import { BusinessError } from '@kit.BasicServicesKit';try {let locationEnabled = geoLocationManager.isLocationEnabled();if (!locationEnabled) {console.warn('Location service is disabled');// 可以拉起全局开关设置弹框,引导用户打开位置开关}
} catch (err) {console.error("errCode:" + err.code + ", message:" + err.message);
}

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

单次获取位置

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

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

如果系统当前没有缓存位置会返回错误码。推荐优先使用该接口获取位置,可以减少系统功耗。

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

import { geoLocationManager } from '@kit.LocationKit';
import { BusinessError } from '@kit.BasicServicesKit';try {let location = geoLocationManager.getLastLocation();console.info('Last location:', JSON.stringify(location));// 检查位置新鲜度const currentTime = Date.now();const locationTime = location.timeStamp;const timeDiff = currentTime - locationTime;if (timeDiff > 5 * 60 * 1000) { // 5分钟console.warn('Location is too old, consider getting fresh location');}
} catch (err) {console.error("errCode:" + JSON.stringify(err));
}

方式二:获取当前位置

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

设置LocatingPriority
  • PRIORITY_ACCURACY:如果对位置的返回精度要求较高,建议优先选择此参数,会将一段时间内精度较好的结果返回给应用
  • PRIORITY_LOCATING_SPEED:如果对定位速度要求较高,建议选择此参数,会将最先拿到的定位结果返回给应用

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

设置locatingTimeoutMs

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

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) => {console.info('Current location: ' + JSON.stringify(result));this.handleLocationResult(result);}).catch((error: BusinessError) => {console.error('Promise, getCurrentLocation: error=' + JSON.stringify(error));this.handleLocationError(error);});
} catch (err) {console.error("errCode:" + JSON.stringify(err));
}// 高精度定位策略示例
let accuracyRequest: geoLocationManager.SingleLocationRequest = {'locatingPriority': geoLocationManager.LocatingPriority.PRIORITY_ACCURACY,'locatingTimeoutMs': 10000
};try {geoLocationManager.getCurrentLocation(accuracyRequest).then((result) => {console.info('Accurate location: ' + JSON.stringify(result));this.handleAccurateLocationResult(result);}).catch((error: BusinessError) => {console.error('Promise, getCurrentLocation: error=' + JSON.stringify(error));});
} catch (err) {console.error("errCode:" + JSON.stringify(err));
}

坐标系说明

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

可参考Map Kit提供的地图计算工具进行坐标转换。

持续定位

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

设置定位参数

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

设置locationScenario

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

设置interval

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

实现示例

地图导航场景
import { geoLocationManager } from '@kit.LocationKit';
import { BusinessError } from '@kit.BasicServicesKit';export class NavigationLocationService {private locationCallback: ((location: geoLocationManager.Location) => void) | null = null;startNavigationLocation(): void {let request: geoLocationManager.ContinuousLocationRequest = {'interval': 1,'locationScenario': geoLocationManager.UserActivityScenario.NAVIGATION};this.locationCallback = (location: geoLocationManager.Location): void => {console.info('Navigation location update: ' + JSON.stringify(location));this.handleNavigationLocation(location);};try {geoLocationManager.on('locationChange', request, this.locationCallback);console.info('Navigation location service started');} catch (err) {console.error("errCode:" + JSON.stringify(err));}}stopNavigationLocation(): void {if (this.locationCallback) {try {geoLocationManager.off('locationChange', this.locationCallback);this.locationCallback = null;console.info('Navigation location service stopped');} catch (err) {console.error("errCode:" + JSON.stringify(err));}}}private handleNavigationLocation(location: geoLocationManager.Location): void {// 处理导航位置更新console.info(`Navigation - Latitude: ${location.latitude}, Longitude: ${location.longitude}`);}
}
运动轨迹记录场景
import { geoLocationManager } from '@kit.LocationKit';export class FitnessTrackingService {private locationCallback: ((location: geoLocationManager.Location) => void) | null = null;private locationHistory: geoLocationManager.Location[] = [];startFitnessTracking(): void {let request: geoLocationManager.ContinuousLocationRequest = {'interval': 3, // 3秒更新一次,适合运动记录'locationScenario': geoLocationManager.UserActivityScenario.FITNESS};this.locationCallback = (location: geoLocationManager.Location): void => {console.info('Fitness location update: ' + JSON.stringify(location));this.recordLocation(location);};try {geoLocationManager.on('locationChange', request, this.locationCallback);console.info('Fitness tracking started');} catch (err) {console.error("errCode:" + JSON.stringify(err));}}stopFitnessTracking(): void {if (this.locationCallback) {try {geoLocationManager.off('locationChange', this.locationCallback);this.locationCallback = null;console.info('Fitness tracking stopped');this.saveLocationHistory();} catch (err) {console.error("errCode:" + JSON.stringify(err));}}}private recordLocation(location: geoLocationManager.Location): void {this.locationHistory.push(location);console.info(`Recorded location ${this.locationHistory.length}: ${location.latitude}, ${location.longitude}`);}private saveLocationHistory(): void {console.info(`Saved ${this.locationHistory.length} location points`);// 保存位置历史到本地存储或云端}
}
出行服务场景
import { geoLocationManager } from '@kit.LocationKit';export class TravelLocationService {private locationCallback: ((location: geoLocationManager.Location) => void) | null = null;startTravelLocation(): void {let request: geoLocationManager.ContinuousLocationRequest = {'interval': 5, // 5秒更新一次,适合出行服务'locationScenario': geoLocationManager.UserActivityScenario.TRAVEL};this.locationCallback = (location: geoLocationManager.Location): void => {console.info('Travel location update: ' + JSON.stringify(location));this.handleTravelLocation(location);};try {geoLocationManager.on('locationChange', request, this.locationCallback);console.info('Travel location service started');} catch (err) {console.error("errCode:" + JSON.stringify(err));}}stopTravelLocation(): void {if (this.locationCallback) {try {geoLocationManager.off('locationChange', this.locationCallback);this.locationCallback = null;console.info('Travel location service stopped');} catch (err) {console.error("errCode:" + JSON.stringify(err));}}}private handleTravelLocation(location: geoLocationManager.Location): void {// 处理出行位置更新console.info(`Travel - Latitude: ${location.latitude}, Longitude: ${location.longitude}`);// 可以用于打车服务、路线规划等}
}

位置信息处理

位置数据结构

interface Location {latitude: number;        // 纬度longitude: number;       // 经度altitude: number;        // 海拔高度accuracy: number;        // 精度timeStamp: number;       // 时间戳direction: number;       // 方向speed: number;          // 速度timeSinceBoot: number;   // 系统启动后的时间
}

位置信息处理示例

export class LocationDataProcessor {processLocation(location: geoLocationManager.Location): void {// 检查位置有效性if (!this.isValidLocation(location)) {console.warn('Invalid location data');return;}// 格式化位置信息const locationInfo = {coordinates: {latitude: location.latitude,longitude: location.longitude},accuracy: location.accuracy,timestamp: new Date(location.timeStamp),speed: location.speed,direction: location.direction};console.info('Processed location:', JSON.stringify(locationInfo));}private isValidLocation(location: geoLocationManager.Location): boolean {return location.latitude !== 0 && location.longitude !== 0 && location.accuracy > 0;}calculateDistance(location1: geoLocationManager.Location, location2: geoLocationManager.Location): number {// 使用Haversine公式计算两点间距离const R = 6371; // 地球半径(公里)const dLat = this.toRadians(location2.latitude - location1.latitude);const dLon = this.toRadians(location2.longitude - location1.longitude);const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +Math.cos(this.toRadians(location1.latitude)) * Math.cos(this.toRadians(location2.latitude)) *Math.sin(dLon / 2) * Math.sin(dLon / 2);const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));return R * c;}private toRadians(degrees: number): number {return degrees * (Math.PI / 180);}
}

错误处理

常见错误码

export class LocationErrorHandler {static handleLocationError(error: BusinessError): void {switch (error.code) {case 201:console.error('Location permission denied');this.handlePermissionError();break;case 202:console.error('Location service unavailable');this.handleServiceUnavailable();break;case 203:console.error('Location timeout');this.handleTimeoutError();break;case 204:console.error('Location service busy');this.handleServiceBusy();break;default:console.error('Unknown location error:', error);this.handleUnknownError(error);}}private static handlePermissionError(): void {// 处理权限错误console.warn('Please grant location permission');}private static handleServiceUnavailable(): void {// 处理服务不可用错误console.warn('Location service is not available');}private static handleTimeoutError(): void {// 处理超时错误console.warn('Location request timeout');}private static handleServiceBusy(): void {// 处理服务繁忙错误console.warn('Location service is busy');}private static handleUnknownError(error: BusinessError): void {// 处理未知错误console.error('Unknown error occurred:', error);}
}

最佳实践

1. 电池优化

export class LocationBatteryOptimizer {private isInBackground: boolean = false;setBackgroundMode(inBackground: boolean): void {this.isInBackground = inBackground;this.optimizeLocationRequest();}private optimizeLocationRequest(): void {if (this.isInBackground) {// 后台时降低定位频率this.updateLocationInterval(10); // 10秒间隔} else {// 前台时正常频率this.updateLocationInterval(1); // 1秒间隔}}private updateLocationInterval(interval: number): void {// 更新定位间隔console.info(`Updated location interval to ${interval} seconds`);}
}

2. 位置缓存管理

export class LocationCacheManager {private static readonly CACHE_DURATION = 5 * 60 * 1000; // 5分钟static isLocationFresh(location: geoLocationManager.Location): boolean {const currentTime = Date.now();const locationTime = location.timeStamp;return (currentTime - locationTime) < this.CACHE_DURATION;}static getOptimalLocation(): Promise<geoLocationManager.Location> {return new Promise((resolve, reject) => {try {// 先尝试获取缓存位置const cachedLocation = geoLocationManager.getLastLocation();if (this.isLocationFresh(cachedLocation)) {resolve(cachedLocation);return;}} catch (error) {console.warn('No cached location available');}// 缓存位置不可用或过期,获取新位置const request: geoLocationManager.SingleLocationRequest = {'locatingPriority': geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED,'locatingTimeoutMs': 5000};geoLocationManager.getCurrentLocation(request).then(resolve).catch(reject);});}
}

注意事项

  1. 及时停止定位:如果不主动结束定位可能导致设备功耗高,耗电快;建议在不需要获取定位信息时及时结束定位
  2. 权限检查:在获取位置前确保已获得相应权限
  3. 位置开关检查:确保系统位置开关已开启
  4. 坐标系转换:注意WGS-84坐标系与其他坐标系的转换
  5. 错误处理:妥善处理定位过程中的各种异常情况
  6. 电池优化:根据应用场景选择合适的定位策略和频率
  7. 隐私保护:合理使用位置信息,保护用户隐私
http://www.xdnf.cn/news/15462.html

相关文章:

  • 每天一个前端小知识 Day 30 - 前端文件处理与浏览器存储机制实践
  • Rust 模块系统:控制作用域与私有性
  • 《[系统底层攻坚] 张冬〈大话存储终极版〉精读计划启动——存储架构原理深度拆解之旅》-系统性学习笔记(适合小白与IT工作人员)
  • 从零开始跑通3DGS教程:(五)3DGS训练
  • React强大且灵活hooks库——ahooks入门实践之常用场景hook
  • 实现“micro 关键字搜索全覆盖商品”并通过 API 接口提供实时数据(一个方法)
  • 【LeetCode数据结构】单链表的应用——反转链表问题、链表的中间节点问题详解
  • DVWA靶场通关笔记-XSS DOM(High级别)
  • Dubbo跨越分布式事务的最终一致性陷阱
  • 一文讲懂填充与步幅
  • AI进化论12:大语言模型的爆发——GPT系列“出圈”,AI飞入寻常百姓家
  • jenkins使用Jenkinsfile部署springboot+docker项目
  • 黑马点评系列问题之p63unlock.lua不知道怎么整
  • 线性代数学习笔记
  • Origin自带的悬浮尺子,Screen Ruler的最佳平替
  • 012_PDF处理与文档分析
  • 【unitrix】 5.0 第二套类型级二进制数基本结构体(types2.rs)
  • sqli-labs靶场通关笔记:第9关 时间盲注
  • NO.5数据结构串和KMP算法|字符串匹配|主串与模式串|KMP|失配分析|next表
  • 前端构建工具 Webpack 5 的优化策略与高级配置
  • 代码随想录算法训练营第十八天
  • Appium源码深度解析:从驱动到架构
  • nginx安装
  • [Subtitle Edit] 语言文件管理.xml | 测试框架(VSTest) | 构建流程(MSBuild) | AppVeyor(CI/CD)
  • COZE token刷新
  • 代码随想录|图论|15并查集理论基础
  • ARC 03 从Github Action job 到 runner pod
  • Java4种设计模式详解(单例模式、工厂模式、适配器模式、代理模式)
  • 【DeepSeek实战】29、金融数据抓取全攻略:从AKShare到API实战,Python量化分析必备指南
  • JavaScript 中一些常见算法的实现及详细解析