HarmonyOS鸿蒙Next中获取经纬度失败,报错显示是权限问题

HarmonyOS鸿蒙Next中获取经纬度失败,报错显示是权限问题 cke_307.png

这是相关权限

cke_2425.png

这是动态申请

cke_5400.png

请问哪里有问题


更多关于HarmonyOS鸿蒙Next中获取经纬度失败,报错显示是权限问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

9 回复

错误码为201:权限验证失败。应用程序不具有调用API所需的权限,当前没有开启权限,开启权限。参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-geolocationmanager-V5#geolocationmanagergetcurrentlocation

需要用代码打开权限弹窗,点击允许:

import { common, abilityAccessCtrl, Permissions, PermissionRequestResult, Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { bundleManager } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

@Entry
@Component
struct Permission {
  @State message: string = 'Hello World';
  @State userGrant: boolean = false
  @State intervalID: number = 0

  async reqPermissionsFromUser(): Promise<number[]> {
    // hilog.info(0x0001, this.TAG, 'reqPermissionsFromUser start ')
    let context = getContext() as common.UIAbilityContext;
    let atManager = abilityAccessCtrl.createAtManager();
    // let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.APP_TRACKING_CONSENT']);
    let grantStatus = await atManager.requestPermissionsFromUser(context,
      ['ohos.permission.APPROXIMATELY_LOCATION', 'ohos.permission.LOCATION']);
    // let grantStatus = await atManager.requestPermissionsFromUser(context, ['ohos.permission.APPROXIMATELY_LOCATION','ohos.permission.LOCATION']);
    return grantStatus.authResults;
  }

  // 申请权限
  async requestLocationPermission() {
    let grantStatus = await this.reqPermissionsFromUser()
    for (let i = 0; i < grantStatus.length; i++) {
      if (grantStatus[i] === 0) {
        // 用户授权,可以继续访问目标操作
        // hilog.info(0x0001, this.TAG, 'requestPermissionsFromUser success');
        this.userGrant = true;
      }
    }
  }

  async aboutToAppear() {
    await this.requestLocationPermission();
  }

  build() {
    RelativeContainer() {
      Text(this.message)
        .id('PermissionHelloWorld')
        .fontSize(50)
        .fontWeight(FontWeight.Bold)
        .alignRules({
          center: { anchor: '__container__', align: VerticalAlign.Center },
          middle: { anchor: '__container__', align: HorizontalAlign.Center }
        })
    }
    .height('100%')
    .width('100%')
  }
}

可以确认下是否正常拉起了权限授权弹窗并授予了权限。

获取经纬度也可以参考下以下示例:

import geoLocationManager from '@ohos.geoLocationManager';
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import { Context } from '@ohos.arkui.UIContext';
import { bundleManager } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';

/**

 系统定位服务实现
 */
@Entry
@Component
struct Index {
  //纬度
  @State lat: Number = -1;
  //经度
  @State lon: Number = -1;

  getLocation() {
    let requestInfo: geoLocationManager.CurrentLocationRequest = {
      'priority': 0x203,
      'scenario': geoLocationManager.LocationRequestScenario.DAILY_LIFE_SERVICE,
      'maxAccuracy': 1000,
      'timeoutMs': 5000
    };
    if (geoLocationManager.isLocationEnabled()) {
      console.log('定位已使能');
    } else {
      console.log('定位未使能');
    }
    try {
      geoLocationManager.getCurrentLocation(requestInfo, (err, location) => {
        if (err) {
          console.error('定位失败,失败原因为:');
          console.error(err.message);
          return;
        }
        console.info('位置获取成功');
        this.lat = location.latitude;
        this.lon = location.longitude;
        console.info("this.lat = " + this.lat);
        console.info("this.lon = " + this.lon);
      })
    } catch (err) {
      console.error('报错原因为:');
      console.error(err);
    }
  }

  requestLocation(context: Context) {
    abilityAccessCtrl.createAtManager().requestPermissionsFromUser(
      context,
      ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION']
    )
  }

  obtainLocation(context: Context) {
    console.log('获取经纬度')
    let permission: Array<Permissions> = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION']
    this.checkPermissionGrant(permission).then((flag: boolean) => {
      if (flag) {
        this.getLocation()
      } else {
        this.requestLocation(context);
      }
    })
  }

  async checkPermissionGrant(permission: Permissions[]): Promise<boolean> {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
    // 获取应用程序的accessTokenID。
    let tokenId: number = 0;
    try {
      let bundleInfo: bundleManager.BundleInfo =
        await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
      let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
      tokenId = appInfo.accessTokenId;
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      console.error(`Failed to get bundle info for self, code: ${err.code}, message: ${err.message}`);
    }
    // 校验应用是否被授予权限。
    try {
      for (let i = 0; i < permission.length; i++) {
        grantStatus = await atManager.checkAccessToken(tokenId, permission[i]);
        if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
          console.log(`没有${permission[i]}权限`)
          return false;
        }
      }
    } catch (error) {
      const err: BusinessError = error as BusinessError;
      console.error(`Failed to check access token, code: ${err.code}, message: ${err.message}`);
    }
    return true;
  }

  build() {
    Column({ space: 20 }) {
      Button('获取位置')
        .backgroundColor(Color.Orange)
        .width(100)
        .height(100)
        .onClick(() => {
          this.obtainLocation(getContext(this))
        })
      Text('经度:' + this.lon);
      Text('纬度:' + this.lat);
    }
    .width('100%')
    .height('100%')
  }
}

更多关于HarmonyOS鸿蒙Next中获取经纬度失败,报错显示是权限问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


位置定位/定位/实时定位

https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-positioning#section189692202585

git:

https://gitee.com/harmonyos_samples/location-service.git

https://gitee.com/openharmony/applications_app_samples.git

定位两种方式:GNSS定位和网络定位

定位方式 说明 优点
GNSS定位 基于全球导航卫星系统,包含GPS、GLONASS、北斗、Galileo等,通过导航卫星、设备芯片提供的定位算法,来确定设备准确位置。 定位精准
网络定位 通过网络进行定位,包括WLAN、蓝牙定位、基站定位。 定位速度快
  • 当前位置定位:获取设备的当前位置信息。开发者可以根据实际需求将其应用于多种业务场景,如外卖定位、打车定位等。
  • 实时位置定位:持续获取设备的实时位置信息。开发者可以将此能力应用于需要实时定位的场景,如步行导航,驾车出行等。
  • 应用后台定位:将应用切换到后台仍然可以持续获取位置信息。该能力可以用于实现后台应用实时记录运动轨迹等业务场景。
  • 历史定位获取:获取系统缓存的最新位置,即最近一次的历史定位信息。该能力可以用于在设备网络信号较弱或对系统功耗比较敏感的场景下获取位置信息。

当前位置定位

  1. 申请定位权限,具体内容可参考申请位置权限开发指导
  2. 实例化位置信息请求对象,确认当前定位策略。以实例化SingleLocationRequest对象为例,将其定位方式优先级设置为快速获取位置优先,定位超时时间设置为10秒,具体代码如下:
  3. 根据定位策略,调用getCurrentLocation()接口获取当前位置信息。
  4. 调用getAddressesFromLocation() 接口进行逆地理编码转化,将位置坐标信息转换为对应的地理位置描述。

实时位置定位

  1. 申请定位权限,具体内容可参考申请位置权限开发指导
  2. 实例化位置信息请求对象,确认持续定位策略。以实例化ContinuousLocationRequest为例,将定位场景类型设置为导航场景,位置信息上报时间间隔设置为1秒,具体代码如下:
  3. 根据定位策略,调用on(‘locationChange’)开启位置变化订阅,并发起定位请求,持续获取当前位置信息。
  4. 在on(‘locationChange’)的回调函数中,调用getAddressesFromLocation()接口进行逆地理编码转化,将坐标信息转换为对应的地理位置描述。
  5. 在不需要获取位置信息时,及时关闭位置变化订阅,并删除对应的定位请求,减少设备功耗。

应用后台定位

应用后台定位需要申请后台定位权限ohos.permission.LOCATION_IN_BACKGROUND和长时任务权限ohos.permission.KEEP_BACKGROUND_RUNNING。申请了相关权限后,开启任务模式为定位导航的长时任务,并在其回调接口中通过on(‘locationChange’)订阅位置变化情况,在应用后台持续获取当前位置信息。

权限配置

  "requestPermissions": [
    {
        "name": "ohos.permission.LOCATION", //用于获取精准位置,精准度在米级别。
        "reason": "$string:permission_reason",
        "usedScene": {"when": "always"}
    },
    {
        "name": "ohos.permission.APPROXIMATELY_LOCATION", //用于获取模糊位置,精确度为5公里。
        "reason": "$string:permission_reason",
        "usedScene": {"when": "always"}
    },
    {
        "name": "ohos.permission.LOCATION_IN_BACKGROUND",//用于应用切换到后台仍然需要获取定位信息的场景。
        "reason": "$string:permission_reason",
        "usedScene": {"when": "always"}
    },
    {
        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
        "reason": "$string:permission_reason",
        "usedScene": {
            "abilities": [
                "EntryAbility"
            ],
            "when": "always"
        }
    }
  ],
"abilities": [
  {
    // ...
    "backgroundModes": [
        "location"
    ],
    // ...
  }
],

Index.ets

import { geoLocationManager } from '@kit.LocationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import hilog from '@ohos.hilog';
import { abilityAccessCtrl, common, wantAgent } from '@kit.AbilityKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';

const TAG = '位置';

@Entry
@Component
struct Index {
  //当前位置
  @State
  curAddress: string = '';
  //实时位置
  @State
  address: string = '';
  //上一次缓存位置
  @State
  cacheAddress: string = '';
  @State
  isOnLocationChange: boolean = false;
  @State
  backIsOn: boolean = false;

  aboutToAppear() {
    abilityAccessCtrl.createAtManager().requestPermissionsFromUser(this.getUIContext().getHostContext(),
      [
        'ohos.permission.LOCATION',
        'ohos.permission.APPROXIMATELY_LOCATION',
      ])
  }

  build() {
    Column() {
      Text("获取当前位置:" + this.curAddress)
        .onClick(() => {
          //实例化位置信息请求对象,确认当前定位策略。以实例化SingleLocationRequest对象为例,将其定位方式优先级设置为快速获取位置优先,定位超时时间设置为10秒
          let request: geoLocationManager.SingleLocationRequest = { locatingPriority: 0x502, locatingTimeoutMs: 10000 };
          geoLocationManager.getCurrentLocation(request).then((location: geoLocationManager.Location) => {
            geoLocationManager.getAddressesFromLocation({
              latitude: location.latitude,
              longitude: location.longitude
            }, async (err, data) => {
              if (data) {

                this.curAddress = data[0]?.placeName || '';
                // ...
              } else {
                hilog.error(0x0000, TAG, `getAddressesFromLocation failed, code: ${err.code}, message: ${err.message}`);

              }
            });
          }).catch((err: BusinessError) => {
            hilog.error(0x0000, TAG, `getCurrentLocationPosition failed, code: ${err.code}, message: ${err.message}`);
            // ...
          });

        })

      Text("获取缓存位置:" + this.cacheAddress)
        .onClick(()=>{
          let location = geoLocationManager.getLastLocation();
          geoLocationManager.getAddressesFromLocation({
            latitude: location.latitude,
            longitude: location.longitude
          }, async (err, data) => {
            if (data) {
              this.cacheAddress = data[0]?.placeName || '';
              // ...
            } else {
              hilog.error(0x0000, TAG, `getAddressesFromLocation failed, code: ${err.code}, message: ${err.message}`);
              // ...
            }
          });
        })


      Toggle({
        type: ToggleType.Button, //按钮 开关
        isOn: this.isOnLocationChange
      }) {
        Text('实时定位' + this.address)
      }.width(100)
      .onChange((isOn: boolean) => {
        this.isOnLocationChange = isOn;
        if (this.isOnLocationChange) {
          this.onLocationChange();
        } else {
          if(!this.backIsOn){
            this.offLocationChange();
          }
        }
      })

      Toggle({
        type: ToggleType.Button, //按钮 开关
        isOn: this.backIsOn
      }) {
        Text('应用后台定位' + this.address)
      }.width(100)
      .onChange((isOn: boolean) => {
        this.backIsOn = isOn;
        if (this.backIsOn) {
          this.startContinuousTask()
        }else {
          this.stopContinuousTask()
        }
      })

    }
    .height('100%')
    .width('100%')
  }

  //关闭长时任务
  stopContinuousTask(): void {
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    if (!context) {
      return;
    }
    backgroundTaskManager.stopBackgroundRunning(context).then(() => {
      if (!this.isOnLocationChange) {
        this.offLocationChange();
      }
      hilog.info(0x0000, TAG, 'stopBackgroundRunning succeeded');
    }).catch((err: BusinessError) => {
      hilog.error(0x0000, TAG, `stopBackgroundRunning failed, cause:  ${JSON.stringify(err)}`);
    });
  }

  //开启长时任务
  startContinuousTask(): void {
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    if (!context) {
      return;
    }
    let wantAgentInfo: wantAgent.WantAgentInfo = {
      wants: [
        {
            bundleName: context.abilityInfo.bundleName,
            abilityName: context.abilityInfo.name
        }
      ],
      operationType: wantAgent.OperationType.START_ABILITY,
      requestCode: 1,
      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    };


    wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj) => {
      backgroundTaskManager.startBackgroundRunning(context,
        backgroundTaskManager.BackgroundMode.LOCATION, wantAgentObj).then(() => {
        this.onLocationChange();
        hilog.info(0x0000, TAG, 'startBackgroundRunning succeeded');
      }).catch((err: BusinessError) => {
        hilog.error(0x0000, TAG, `startBackgroundRunning failed, cause:  ${JSON.stringify(err)}`);
      });
    });
  }

  offLocationChange() {
    try {
      geoLocationManager.off('locationChange');
    } catch (err) {
      hilog.error(0x0000, TAG, `offLocationChange failed, code: ${err.code}, message: ${err.message}`);
    }
  }

  onLocationChange() {
    let request: geoLocationManager.ContinuousLocationRequest = {
      interval: 0, //上报时间间隔默认1    不动动时候20秒一次 表示上报位置信息的时间间隔,单位是秒。默认值为1,取值范围为大于等于0。等于0时对位置上报时间间隔无限制。
      locationScenario: 0x401
    };
    geoLocationManager.on('locationChange', request, this.locationChange);
  }

  //必须抽成方法 这样on('locationChange')可以注册多次只会执行一次,且
  locationChange = (location: geoLocationManager.Location): void =>  {
    console.log('locationChange: ' + JSON.stringify(location));
    geoLocationManager.getAddressesFromLocation({
      latitude: location.latitude,
      longitude: location.longitude
    }, async (err, data) => {
      if (data) {
        console.log('locationChange: ' );
        this.address = (data[0]?.placeName || '') ;
        // ...
      } else {
        hilog.error(0x0000, TAG, `getAddressesFromLocation failed, code: ${err.code}, message: ${err.message}`);
        // ...
      }
    });
  }
}

【背景知识】

应用在使用位置服务系统能力前,需要检查是否已经获取用户授权访问设备位置信息。如未获得授权,可以向用户申请需要的位置权限。

系统提供的定位权限有:

  • ohos.permission.LOCATION:用于获取精准位置,精准度在米级别。
  • ohos.permission.APPROXIMATELY_LOCATION:用于获取模糊位置,精确度为5公里。
  • ohos.permission.LOCATION_IN_BACKGROUND:用于应用切换到后台仍然需要获取定位信息的场景。

【解决方案】

如果应用在后台运行时也需要访问设备位置,需要申请ohos.permission.LOCATION_IN_BACKGROUND权限或申请LOCATION类型的长时任务,这样应用在切入后台之后,系统可以继续上报位置信息。

应用如需使用后台位置权限,需要在设置界面由用户手动授予,具体授权方式请参考ohos.permission.LOCATION_IN_BACKGROUND的权限说明

长时任务申请可参考长时任务

开发者可以在应用配置文件中声明所需要的权限,具体可参考向用户申请授权

  1. 未动态申请用户授权
    • ohos.permission.LOCATION属于用户授权类型(user_grant),需在代码中主动申请:
      import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
      
      let atManager = abilityAccessCtrl.createAtManager();
      try {
      atManager.requestPermissionsFromUser(this.context, ["ohos.permission.LOCATION"])
      .then((result) => {
      if (result.authResults === 0) {
      console.info("权限已授予");
      this.getLocation(); // 调用获取经纬度方法
      }
      });
      } catch (err) {
      console.error("权限申请失败:" + err);
      }
      

201,通常是由于权限声明或动态申请流程未正确处理导致的

在代码中调用以下接口动态申请权限:

import abilityAccessCtrl from '@kit.AbilityAccessCtrlKit';

async function requestLocationPermission() {
  try {
    const atManager = abilityAccessCtrl.createAtManager();
    const permissions: Array<string> = ['ohos.permission.LOCATION'];
    const result = await atManager.requestPermissionsFromUser(globalThis.abilityContext, permissions);
    if (result.authResults === 0) {
      console.log('权限申请成功');
      // 开始获取经纬度
    } else {
      console.error('用户拒绝授权');
    }
  } catch (err) {
    console.error(`权限申请失败, 错误码: ${err.code}`);
  }
}

看了你的问题,应该是没有弹窗获取用户授权。

建议参考我的代码,通过异步的方式正确获取定位权限以后再去定位:

async reqPermissionsFromUser(permissions: Array<Permissions>): Promise<boolean> {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
    // console.info(`授权在系统设置`)
    return new Promise<boolean>(async (resolve)=>{
      atManager.requestPermissionsFromUser(context, permissions).then((data: PermissionRequestResult) => {
        let grantStatus: Array<number> = data.authResults;
        let length: number = grantStatus.length;
        for (let i = 0; i < length; i++) {
          if (grantStatus[i] === 0) {
            // 用户授权,可以继续访问目标操作
            resolve(true)
          } else {
            // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
            console.info(`授权在系统设置`)
            resolve(false)
            return;
          }
        }
        // 授权成功
        console.info(`授权成功`)
      }).catch((err: BusinessError) => {
        console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`);
        resolve(false)
      })
    })

  }
 //具体使用
 let grantList: Permissions[] = ['ohos.permission.APPROXIMATELY_LOCATION','ohos.permission.LOCATION']
 
await reqPermissionsFromUser(grantList)

用户权限吧?

在HarmonyOS Next中获取经纬度失败且提示权限问题,通常是由于未正确配置位置权限所致。请检查应用是否在config.json中声明了ohos.permission.LOCATION权限,并确保在代码中动态请求用户授权。若权限已配置但仍失败,需确认设备定位服务已开启且应用拥有前台或后台定位权限。

根据您提供的截图,问题可能出现在权限申请流程中。在HarmonyOS Next中,获取位置权限需要正确配置权限声明和动态请求。

  1. 检查config.json中是否已声明ohos.permission.LOCATION权限,并确保权限级别(如system_grantuser_grant)与使用场景匹配。

  2. 动态申请权限时,需使用requestPermissionsFromUser方法,并在回调中处理授权结果。请确认代码中已正确实现权限请求逻辑,包括用户拒绝时的处理。

  3. 确保设备位置服务已开启,且应用在后台获取位置时还需申请ohos.permission.LOCATION_IN_BACKGROUND权限。

建议检查权限声明和动态请求代码是否完整,并验证设备设置。

回到顶部