HarmonyOS应用开发实战:基于ArkTS实现定位权限管理与实时位置追踪

注:适用版本(Harmony OS NEXT / 5.0 / API 12+ )

一、效果展示

                                                

二、实现逻辑

  • 获取权限管理

    通过 abilityAccessCtrl 模块实现动态权限申请,遵循最小化授权原则(仅请求 APPROXIMATELY_LOCATION 和 LOCATION 权限),并在异步回调中通过 authResults 统一校验授权状态。这一层确保功能符合系统安全规范,避免因权限缺失导致服务中断

  • 定位服务封装

        将定位核心逻辑(如请求参数配置、事件监听)抽象为 LocationService 静态类

  • 数据流向清晰化

    locationChange 事件触发 → 回调函数捕获位置对象 → 更新 text_locationResult 状态变量 → 触发 UI 重渲染。形成 ​​「事件→数据→视图」​ 单向数据流,保障可预测性

  • 异常监控

    try-catch 块包裹关键操作(如权限请求、事件注册),结合 hilog 日志工具输出错误详情,便于快速定位问题。

三、源码

        1、静态源码
// 导入所需的模块
import { geoLocationManager } from '@kit.LocationKit'; // 用于管理地理位置功能
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit'; // 用于权限管理
import { hilog } from '@kit.PerformanceAnalysisKit'; // 用于日志记录

// LocationService类封装了与定位相关的操作
class LocationService {
  // 请求定位权限
  static async requestPermissions(): Promise<boolean> {
    // 定义需要的权限列表
    const permissions: Array<Permissions> = ['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION'];
    // 创建权限管理对象
    const atManager = abilityAccessCtrl.createAtManager();
    try {
      // 向用户请求权限
      const result = await atManager.requestPermissionsFromUser(getContext(), permissions);
      // 检查所有权限是否都被授予
      return result.authResults.every(status => status === 0);
    } catch (err) {
      // 如果请求失败,抛出错误
      throw new Error(`requestPermissionsFromUser failed: ${JSON.stringify(err)}`);
    }
  }

  // 开始定位跟踪
  static startLocationTracking(callback: (location: geoLocationManager.Location) => void) {
    // 定义定位请求参数
    const requestInfo: geoLocationManager.LocationRequest = {
      'scenario': geoLocationManager.LocationRequestScenario.DAILY_LIFE_SERVICE, // 定位场景
      'priority': geoLocationManager.LocationRequestPriority.FIRST_FIX, // 定位优先级
      'timeInterval': 1, // 时间间隔
      'distanceInterval': 0, // 距离间隔
      'maxAccuracy': 0 // 最大精度
    };
    try {
      // 注册位置变化监听器
      geoLocationManager.on('locationChange', requestInfo, callback);
    } catch (err) {
      // 如果启动定位失败,抛出错误
      throw new Error(`startLocationTracking failed: ${JSON.stringify(err)}`);
    }
  }

  // 停止定位跟踪
  static stopLocationTracking(callback: (location: geoLocationManager.Location) => void) {
    // 取消位置变化监听器
    geoLocationManager.off('locationChange', callback);
  }

  // ... 其他定位相关方法 ...
}

// 定义定位页面组件
@Entry
@Component
struct PositioningPage {
  @State text_locationResult: string = ''; // 用于显示定位结果的文本
  @State isTracking: boolean = false; // 标记是否正在定位

  // 构建页面布局
  build() {
    Column({ space: 20 }) {
      // 权限获取按钮
      Button('获取定位权限')
        .type(ButtonType.Capsule) // 设置按钮样式为胶囊形状
        .onClick(async () => {
          try {
            // 请求定位权限
            const granted = await LocationService.requestPermissions();
            // 根据权限请求结果更新显示文本
            this.text_locationResult = granted ? '权限获取成功' : '权限获取失败';
          } catch (err) {
            // 如果请求失败,处理错误
            this.handleError(err);
          }
        })

      // 定位控制按钮
      Row({ space: 20 }) {
        Button(this.isTracking ? '停止定位' : '开始定位')
          .type(ButtonType.Capsule) // 设置按钮样式为胶囊形状
          .backgroundColor(this.isTracking ? '#ff4d4f' : '#52c41a') // 根据定位状态设置按钮背景色
          .onClick(() => {
            if (this.isTracking) {
              // 如果正在定位,停止定位
              LocationService.stopLocationTracking(this.locationChange);
              this.text_locationResult = '定位已停止';
            } else {
              try {
                // 如果未在定位,开始定位
                LocationService.startLocationTracking(this.locationChange);
                this.text_locationResult = '定位已开始';
              } catch (err) {
                // 如果启动定位失败,处理错误
                this.handleError(err);
              }
            }
            // 切换定位状态
            this.isTracking = !this.isTracking;
          })
      }

      // 结果显示区域
      Scroll() {
        Text(this.text_locationResult)
          .fontSize(16) // 设置文本字体大小
          .textAlign(TextAlign.Start) // 设置文本对齐方式
          .padding(10) // 设置文本内边距
      }
      .height('40%') // 设置滚动区域高度
      .width('100%') // 设置滚动区域宽度
      .border({ width: 1, color: '#d9d9d9' }) // 设置边框样式
      .margin({ top: 20 }) // 设置上边距
    }
    .padding(20) // 设置列的内边距
    .width('100%') // 设置列的宽度
    .height('100%') // 设置列的高度
  }

  // 处理位置变化的回调函数
  private locationChange = (location: geoLocationManager.Location) => {
    // 更新定位结果文本
    this.updateLocationResult(`当前位置: ${location.latitude},${location.longitude}`);
    // 记录调试日志
    hilog.debug(0x0000, 'testTag', 'locationChange: %{public}s', JSON.stringify(location));
  }

  // 更新定位结果文本
  private updateLocationResult(result: string) {
    // 在结果文本前添加时间戳
    this.text_locationResult = `${new Date().toLocaleTimeString()}: ${result}\n${this.text_locationResult}`;
  }

  // 处理错误信息
  private handleError(err: Error) {
    // 记录错误日志
    hilog.error(0x0000, 'testTag', `Error: ${err.message}`);
    // 更新显示文本为错误信息
    this.text_locationResult = `错误: ${err.message}`;
  }
}
        2、权限源码
 "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "$string:EntryAbility_label",
        "usedScene": {

        }
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "$string:EntryAbility_label",
        "usedScene": {

        }
      }
    ],

四、源码详细分析

const permissions: Permissions[] = [
  'ohos.permission.APPROXIMATELY_LOCATION', // 必须声明于config.json
  'ohos.permission.LOCATION' // 精确定位需同时申请
];

// 动态请求实现
static async requestPermissions() {
  const atManager = abilityAccessCtrl.createAtManager();
  const result = await atManager.requestPermissionsFromUser(
    getContext(), // 关键:依赖Ability上下文
    permissions
  );
  return result.authResults.every(status => status === 0); // 0为授权成功
}
  1. 双层权限体系:鸿蒙要求同时申请粗略+精确定位权限

  2. 运行时请求:通过createAtManager创建权限管理器实例

  3. 上下文依赖getContext()必须能获取到有效的Ability上下文

  4. 结果验证authResults数组顺序需与请求权限顺序一致

const requestInfo: geoLocationManager.LocationRequest = {
  scenario: LocationRequestScenario.DAILY_LIFE_SERVICE, // 场景策略
  priority: LocationRequestPriority.FIRST_FIX,          // 定位策略
  timeInterval: 1,      // 单位:秒(实际受系统策略限制)
  distanceInterval: 0,   // 单位:米(0表示仅时间间隔生效)
  maxAccuracy: 0         // 精度要求(0为最高)
};
参数可选值影响维度推荐场景
scenarioUN_SET / NAVIGATION /DAILY_LIFE_SERVICE系统资源分配策略导航场景选NAVIGATION
priorityACCURACY / FIRST_FIX / LOW_POWER首次定位速度与精度快速定位用FIRST_FIX
timeInterval≥1 (秒)最小更新间隔结合业务需求调整
distanceInterval≥0 (米)位移触发阈值

五、错误的处理

        1、错误一(展示)

                                        ​​​​​​​        ​​​​​​​        

                产生原因:系统获取了定位权限,但手机并没有开启定位功能

                解决方法:打开手机定位权限

                 参考文档:3301100 位置功能的开关未开启导致功能失败

        2、错误二(展示)

                                                          

                产生原因:未配置系统的权限管理(module.json5)

                解决方法:配置系统定位权限

                参考文档:通用错误码-API参考概述 - 华为HarmonyOS开发者

六、总结

这功能就像给你的手机装了个“小雷达”,主要做三件事:

  1. 先问权限
    用之前会弹窗问用户“我要用你的位置哈,行不?”,用户同意了才能继续,跟微信要定位发位置是一个道理。

  2. 开关定位
    点“开始定位”按钮,手机就开始偷偷干活——每隔1秒或者动一下就记下你现在的经纬度(比如北纬30度,东经120度),然后把这些数字显示在屏幕上。不想用了就点“停止”,立马省电不耗流量。

  3. 实时显示+记录
    只要开着定位,屏幕上就会像刷朋友圈一样,从上往下滚动显示最新的位置记录。哪里出错了(比如没开GPS),也会直接大字报提醒“出问题啦,具体原因是XXX”。

核心就一句话:先要权限再干活,点按钮就开/关定位,位置变就刷新屏幕,出问题直接告诉你哪儿不对劲。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值