文章目录前言一、HarmonyOS 权限分级二、声明权限module.json5为什么需要两个定位权限三、PermissionsUtil 完整解析四、权限检查流程详解4.1 第一步获取 accessTokenId4.2 第二步检查权限状态4.3 第三步发起权限申请弹窗五、在 MainPage 里使用5.1 aboutToAppear 是什么时机5.2 为什么在主页申请权限而不在地图页六、完整权限申请流程图七、权限被拒绝怎么处理总结前言附近加油站要知道用户在哪所以需要定位权限。在 HarmonyOS 上权限不是你想要就能要的——用户必须主动授权。这篇文章把PermissionsUtil.ets拆开来讲带你理解从权限声明到运行时申请的完整流程也讲清楚这个封装的设计思路。项目预览一、HarmonyOS 权限分级HarmonyOS 的权限分三个等级等级名称特点示例normal普通权限安装时自动授予用户无感知网络访问、振动system_basic系统基础权限运行时需用户授权精确定位、相机、麦克风system_core系统核心权限仅系统应用可用系统设置修改等定位权限是system_basic级别必须在运行时弹窗让用户同意。二、声明权限module.json5使用任何权限前必须先在module.json5里声明// entry/src/main/module.json5{module:{requestPermissions:[{name:ohos.permission.LOCATION,reason:$string:permission_reason,usedScene:{abilities:[EntryAbility],when:inuse}},{name:ohos.permission.APPROXIMATELY_LOCATION,reason:$string:permission_reason,usedScene:{abilities:[EntryAbility],when:inuse}}]}}字段说明字段说明name权限名称系统定义的字符串reason申请原因必须是字符串资源不能写死否则上架审核不通过usedScene.wheninuse使用时或always始终定位通常用inuse为什么需要两个定位权限// MainPage.etsconstPERMISSIONS:ArrayPermissions[ohos.permission.LOCATION,// 精确定位GPS级别精度约10米ohos.permission.APPROXIMATELY_LOCATION// 模糊定位网络/基站精度约1公里];权限精度说明APPROXIMATELY_LOCATION~1000m模糊定位申请精确定位的前提LOCATION~10m精确定位GPS依赖模糊定位权限HarmonyOS 规定申请精确定位LOCATION必须同时申请模糊定位APPROXIMATELY_LOCATION。用户可以只授予模糊定位拒绝精确定位。三、PermissionsUtil 完整解析// entry/src/main/ets/utils/PermissionsUtil.etsimport{abilityAccessCtrl,bundleManager,Permissions}fromkit.AbilityKit;import{BusinessError}fromkit.BasicServicesKit;import{Logger}from./Logger;import{geoLocationManager}fromkit.LocationKit;exportclassPermissionsUtil{/** * 检查并申请权限 * param permissions 要检查的权限数组 * param context UIAbilityContext */asynccheckPermissions(permissions:ArrayPermissions,context:Context):Promisevoid{letapplyResult:booleanfalse;// 遍历每个权限检查是否已授权for(letpermissionofpermissions){letgrantStatus:abilityAccessCtrl.GrantStatusawaitthis.checkAccessToken(permission);if(grantStatusabilityAccessCtrl.GrantStatus.PERMISSION_GRANTED){applyResulttrue;}else{applyResultfalse;}}// 如果有任何一个权限未授权则发起权限申请if(!applyResult){this.requestPermissions(permissions,context);}}/** * 检查单个权限的授权状态 */asynccheckAccessToken(permission:Permissions):PromiseabilityAccessCtrl.GrantStatus{letatManager:abilityAccessCtrl.AtManagerabilityAccessCtrl.createAtManager();letgrantStatus:abilityAccessCtrl.GrantStatusabilityAccessCtrl.GrantStatus.PERMISSION_DENIED;// 第一步获取应用的 accessTokenIdlettokenId:number0;try{letbundleInfo:bundleManager.BundleInfoawaitbundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);letappInfo:bundleManager.ApplicationInfobundleInfo.appInfo;tokenIdappInfo.accessTokenId;}catch(error){Logger.error(Failed to get bundle info for self. Code is${error.code}, message is${error.message});}// 第二步使用 tokenId 检查权限是否已授予try{grantStatusawaitatManager.checkAccessToken(tokenId,permission);}catch(error){Logger.error(Failed to check access token. Code is${error.code}, message is${error.message});}returngrantStatus;}/** * 发起运行时权限申请 */requestPermissions(permissions:ArrayPermissions,context:Context):void{letatManager:abilityAccessCtrl.AtManagerabilityAccessCtrl.createAtManager();atManager.requestPermissionsFromUser(context,permissions).then((){// 用户同意了权限立即获取一次位置激活定位服务geoLocationManager.getCurrentLocation();Logger.info(request Permissions success);}).catch((err:BusinessError){Logger.error(Failed to request permissions from user. Code is${err.code}, message is${err.message});});}}四、权限检查流程详解4.1 第一步获取 accessTokenIdletbundleInfo:bundleManager.BundleInfoawaitbundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);lettokenIdbundleInfo.appInfo.accessTokenId;accessTokenId是应用的唯一标识符token权限管理系统用它来标识是哪个应用在问权限。通过getBundleInfoForSelf获取当前应用的包信息再从中取出tokenId。4.2 第二步检查权限状态letatManagerabilityAccessCtrl.createAtManager();letgrantStatusawaitatManager.checkAccessToken(tokenId,permission);GrantStatus有两个值enumGrantStatus{PERMISSION_DENIED-1,// 未授权用户拒绝或未曾申请PERMISSION_GRANTED0// 已授权}4.3 第三步发起权限申请弹窗atManager.requestPermissionsFromUser(context,permissions).then((){...}).catch((err){...});这行代码会弹出系统权限申请对话框让用户选择允许仅本次 / 使用时允许 / 始终允许拒绝提示.then()在用户点击允许后触发.catch()在用户点击拒绝后触发。注意这里不管用户选择了什么级别的允许仅本次/使用时/始终都会进入.then()分支。真正判断权限是否生效还是要再次调用checkAccessToken。五、在 MainPage 里使用// MainPage.etsconstPERMISSIONS:ArrayPermissions[ohos.permission.LOCATION,ohos.permission.APPROXIMATELY_LOCATION];EntryComponentstruct MainPage{privatecontext:Contextthis.getUIContext().getHostContext()ascommon.UIAbilityContext;asyncaboutToAppear():Promisevoid{letpermissionsUtilnewPermissionsUtil();permissionsUtil.checkPermissions(PERMISSIONS,this.context);}// ...}5.1 aboutToAppear 是什么时机aboutToAppear是 ArkUI 组件的生命周期方法在组件即将显示挂载到 UI 树上之前调用。这是做初始化工作的好地方。5.2 为什么在主页申请权限而不在地图页好问题。在主页而不是在真正需要定位的地图页申请权限是一种提前申请策略用户打开主页时就弹权限申请用户有时间在进入地图前决定是否授权避免用户刚打开地图就连续弹出多个对话框地图加载 权限申请这是 UX 上的考量让用户体验更流畅。六、完整权限申请流程图MainPage.aboutToAppear() └─ PermissionsUtil.checkPermissions([LOCATION, APPROXIMATELY_LOCATION], context) │ ├─ checkAccessToken(LOCATION) │ └─ 获取 tokenId │ └─ atManager.checkAccessToken(tokenId, LOCATION) │ ├─ PERMISSION_GRANTED → applyResult true │ └─ PERMISSION_DENIED → applyResult false │ ├─ checkAccessToken(APPROXIMATELY_LOCATION) │ └─ ... 同上 │ └─ 如果 applyResult false └─ requestPermissions([LOCATION, APPROXIMATELY_LOCATION], context) ├─ 弹出权限申请对话框 ├─ 用户允许 → geoLocationManager.getCurrentLocation() 激活定位 └─ 用户拒绝 → 记录错误日志应用无法获取位置七、权限被拒绝怎么处理这个项目里权限被拒绝只是记了日志实际产品中应该更友好// 更完善的权限申请处理atManager.requestPermissionsFromUser(context,permissions).then((result){// 检查每个权限的结果letisGrantedresult.authResults.every(authauthabilityAccessCtrl.GrantStatus.PERMISSION_GRANTED);if(isGranted){Logger.info(所有权限已授予);}else{// 引导用户去设置页手动开启this.showPermissionGuideDialog();}}).catch((err:BusinessError){Logger.error(权限申请失败:${err.message});});// 引导用户去设置页privateshowPermissionGuideDialog(){// 弹窗告知用户需要手动开启权限AlertDialog.show({title:需要位置权限,message:附近加油站需要获取您的位置才能使用请在设置中开启位置权限。,confirm:{value:去设置,action:(){// 跳转到应用设置页letwant:Want{bundleName:com.huawei.hmos.settings,abilityName:com.huawei.hmos.settings.MainAbility,};this.context.startAbility(want);}}});}总结HarmonyOS 权限申请分三步声明在module.json5的requestPermissions里声明需要的权限检查用abilityAccessCtrl.AtManager的checkAccessToken检查是否已授权申请用requestPermissionsFromUser发起运行时权限弹窗PermissionsUtil把这三步封装成一个checkPermissions方法调用方只需要传权限列表和 context其余都自动处理。下一篇讲数据模型——StationData接口和STATION_LIST静态数据是怎么设计的。