HarmonyOS鸿蒙Next中如何优雅处理权限请求?
HarmonyOS鸿蒙Next中如何优雅处理权限请求? 问题描述
在HarmonyOS开发中,权限管理是应用安全的重要组成部分。在应用中,需要处理各种权限请求,如位置权限、存储权限等。
自定义一个PermissionHelper权限管理工具类,封装权限检查和请求的逻辑,提供批量权限校验、动态权限申请等功能,简化权限管理的复杂度,
代码示例:
import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { LogUtils } from './LogUtils';
let TAG = "PermissionHelper"
const context = getContext(this) as common.UIAbilityContext;
export interface PermissionGrantStatusResult{
permissionsAll:Permissions[];
permissionsDenied:Permissions[];//被拒绝
grantStatus:boolean;
}
/**
* @author J.query
* @date 2024/12/5 10:34
* @email j-query@foxmail.com
* Description:
*/
export default class PermissionHelper {
//
/**
* 批量校验权限
* -1 PERMISSION_DENIED表示未授权。0 PERMISSION_GRANTED表示已授权。1 PERMISSION_GRANTED_BACKGROUND_LIMITED表示临时授权。
* @param permissionsArray
* @returns
*/
static async checkPermissionsArray(permissionsArray: Array<Permissions>): Promise<PermissionGrantStatusResult> {
//LogUtils.debug(TAG,'checkPermissionsArray permissionArray:' + JSON.stringify(permissionsArray));
let authResults = new Array<number>(permissionsArray.length)
let permissionsDenied: Array<Permissions> = new Array()
permissionsArray.forEach((value, index) => {
const grantStatus: abilityAccessCtrl.GrantStatus = PermissionHelper.checkPermissionGrant(value);
authResults[index] = grantStatus;
if (grantStatus != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
permissionsDenied.push(permissionsArray[index])
}
})
let result = {
"permissionsAll": permissionsArray,
"permissionsDenied": permissionsDenied,
"grantStatus": permissionsDenied.length <= 0
} as PermissionGrantStatusResult
return result
}
/**
* 动态申请权限 二次请求 api12中提供的
* @param permissionArray
* @returns 相应请求权限的结果。
*/
static async requestPermissionsArray(permissionArray: Array<Permissions>): Promise<PermissionGrantStatusResult>{
LogUtils.debug(TAG,'requestPermissionsArray permissionArray:' + JSON.stringify(permissionArray));
let result = await PermissionHelper.reqPermissionsArrayFromUser(permissionArray)
if (!result.grantStatus) {
result = await PermissionHelper.requestPermissionsArrayOnSetting(result.permissionsDenied)
}
LogUtils.debug(TAG,'requestPermissionsArray permissionArray:' + JSON.stringify(result));
return result
}
/**
* 校验应用是否被授予 {permission} 权限
* @param permission
* @returns
*/
private static checkPermissionGrant(permission: Permissions): abilityAccessCtrl.GrantStatus {
const atManager = abilityAccessCtrl.createAtManager();
// 初始化grantStatus为未授权
let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
let tokenId: number = 0;
try {
// 获取应用程序的accessTokenID
const bundleInfo: bundleManager.BundleInfo =
bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
const appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
tokenId = appInfo.accessTokenId;
// 校验应用是否被授予权限
grantStatus = atManager.checkAccessTokenSync(tokenId, permission);
LogUtils.debug(TAG, `checkPermissionGrant 检查${permission},grantStatus=${grantStatus},权限状态为:${grantStatus ===
abilityAccessCtrl.GrantStatus.PERMISSION_DENIED ? "未授权" : "已授权"}`);
} catch (error) {
const err = error as BusinessError;
LogUtils.debug(TAG, `checkPermissionGrant检查${permission}权限异常:${JSON.stringify(err)}`);
grantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
}
return grantStatus;
}
/**
* 动态申请权限 首次请求
* @param permissionsArray
* @returns
*/
private static async reqPermissionsArrayFromUser(permissionsArray: Array<Permissions>): Promise<PermissionGrantStatusResult> {
LogUtils.debug(TAG, 'reqPermissionArrayFromUser permissionArray:' + JSON.stringify(permissionsArray));
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let permissionsDenied: Array<Permissions> = new Array()
return atManager.requestPermissionsFromUser(context, permissionsArray).then((data) => {
LogUtils.debug(TAG, 'requestPermissionsFromUser:' + JSON.stringify(data));
let grantStatus: Array<number> = data.authResults;
let permissions: Array<string> = data.permissions;
let dialogShownResults: Array<boolean> | undefined = data.dialogShownResults
permissions.forEach((value, index) => {
let dialogShown = false
let grantStatusTxt = "未知"
if (dialogShownResults != undefined) {
dialogShown = dialogShownResults[index]
}
//值0表示授权,值-1表示不授权,值2表示请求无效。
if (grantStatus[index] == 0) {
grantStatusTxt = "授权"
} else if (grantStatus[index] == -1) {
grantStatusTxt = "不授权"
} else if (grantStatus[index] == 2) {
grantStatusTxt = "请求无效"
}
LogUtils.debug(TAG,
`requestPermissionsFromUser [${value},${grantStatusTxt},${dialogShown ? "已经弹窗" : "没有弹窗"}]`);
if (grantStatus[index] === -1 && !dialogShown) {
permissionsDenied.push(permissionsArray[index])
} else if (grantStatus[index] === 2) {
throw new Error("请求无效")
}
})
let result = {
"permissionsAll": permissionsArray,
"permissionsDenied": permissionsDenied,
"grantStatus": permissionsDenied.length <= 0
} as PermissionGrantStatusResult
return result
}).catch((err: BusinessError) => {
LogUtils.debug(TAG, `reqPermissionArrayFromUser err:${err?.message},${JSON.stringify(permissionsArray)}`);
let result = {
"permissionsAll": permissionsArray,
"permissionsDenied": permissionsArray,
"grantStatus": false
} as PermissionGrantStatusResult
return result
});
}
/**
* 动态申请权限 二次请求 api12中提供
* @param permissionsArray
* @returns 相应请求权限的结果。值0表示授权,值-1表示不授权
*/
private static async requestPermissionsArrayOnSetting(permissionsArray: Array<Permissions>): Promise<PermissionGrantStatusResult>{
let permissionsDenied: Array<Permissions> = new Array()
LogUtils.debug(TAG,'requestPermissionOnSetting permissionArray:' + JSON.stringify(permissionsArray));
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
return atManager.requestPermissionOnSetting(context, permissionsArray).then((data: Array<abilityAccessCtrl.GrantStatus>) => {//api12中提供
LogUtils.debug(TAG,'requestPermissionOnSetting data:' + JSON.stringify(data));
permissionsArray.forEach((value,index)=>{
LogUtils.debug(TAG,`requestPermissionOnSetting [${value},${data[index] === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED ? "用户拒绝授权" : "用户授权"}]` );
if (data[index]===abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
permissionsDenied.push(permissionsArray[index])
}
})
let result = {"permissionsAll":permissionsArray,
"permissionsDenied" :permissionsDenied,
"grantStatus":permissionsDenied.length<=0} as PermissionGrantStatusResult
return result
}).catch((err: BusinessError) => {
LogUtils.debug(TAG,`reqPermissionArrayFromUser err:${err?.message},${JSON.stringify(permissionsArray)}`);
let result = {"permissionsAll":permissionsArray,
"permissionsDenied" :permissionsArray,
"grantStatus":false} as PermissionGrantStatusResult
return result
});
}
}
详细回答
Q1: PermissionHelper.ets是什么?
A1: PermissionHelper.ets是HarmonyOS应用中的权限管理工具类,主要封装了权限检查和请求的逻辑,提供批量权限校验、动态权限申请等功能,简化了权限管理的复杂度。
Q2: PermissionHelper.ets的主要功能是什么?
A2:
- 批量校验权限: checkPermissionsArray - 检查多个权限的授权状态
- 动态申请权限: requestPermissionsArray - 请求权限,支持二次请求
- 权限状态检查: checkPermissionGrant - 检查单个权限是否被授予
Q3: 如何使用PermissionHelper进行权限检查?
A3:
import { Permissions } from '@kit.AbilityKit';
import PermissionHelper from './PermissionHelper';
// 示例1:检查位置权限
async function checkLocationPermission() {
const permissionsArray: Permissions[] = [
'ohos.permission.APPROXIMATELY_LOCATION' ,
'ohos.permission.LOCATION'
];
try {
const result = await PermissionHelper.checkPermissionsArray(permissionsArray);
if (result.grantStatus) {
console.log('位置权限已获得');
// 执行需要位置权限的操作
} else {
console.log('位置权限未获得');
// 请求权限
await requestLocationPermission();
}
} catch (error) {
console.error('检查位置权限时出错:', error);
}
}
// 示例2:请求位置权限
async function requestLocationPermission() {
const permissionsArray: Permissions[] = [
'ohos.permission.APPROXIMATELY_LOCATION' ,
'ohos.permission.LOCATION'
];
try {
const result = await PermissionHelper.requestPermissionsArray(permissionsArray);
if (result.grantStatus) {
console.log('位置权限请求成功');
// 执行需要位置权限的操作
} else {
console.log('位置权限请求失败');
console.log('被拒绝的权限:', result.permissionsDenied);
}
} catch (error) {
console.error('请求位置权限时出错:', error);
}
}
// 示例3:批量检查多个权限
async function checkMultiplePermissions() {
const permissionsArray: Permissions[] = [
'ohos.permission.APPROXIMATELY_LOCATION',
'ohos.permission.LOCATION' ,
'ohos.permission.STORAGE_MANAGER'
];
try {
const result = await PermissionHelper.checkPermissionsArray(permissionsArray);
console.log('所有权限:', result.permissionsAll);
console.log('被拒绝的权限:', result.permissionsDenied);
console.log('是否全部授权:', result.grantStatus);
if (!result.grantStatus) {
// 请求被拒绝的权限
const requestResult = await PermissionHelper.requestPermissionsArray(result.permissionsDenied);
console.log('权限请求结果:', requestResult);
}
} catch (error) {
console.error('处理权限时出错:', error);
}
}
// 示例4:位置权限使用示例
async function requestLocationPermissions() {
// 使用正确的权限类型
const locationPermissions: Permissions[] = [
'ohos.permission.APPROXIMATELY_LOCATION',
'ohos.permission.LOCATION'
];
try {
const result = await PermissionHelper.checkPermissionsArray(locationPermissions);
console.log('位置权限检查结果:', result);
if (!result.grantStatus) {
console.log('请求位置权限...');
const requestResult = await PermissionHelper.requestPermissionsArray(locationPermissions);
console.log('位置权限请求结果:', requestResult);
} else {
console.log('位置权限已获得');
}
} catch (error) {
console.error('处理位置权限时出错:', error);
}
}
export {
checkLocationPermission,
requestLocationPermission,
checkMultiplePermissions,
requestLocationPermissions
};
效果
使用PermissionHelper.ets的优势
- 统一的权限管理: 所有权限请求都通过统一的接口处理,便于维护
- 批量处理能力: 支持一次性检查或请求多个权限,提高效率
- 自动重试机制: 对于被拒绝的权限,提供二次请求机会
- 详细的权限状态: 提供具体的被拒绝权限列表,便于针对性处理
- 错误处理完善: 包含完整的异常处理逻辑,提高应用稳定性
实际应用场景
在HarmonyOS应用中,使用PermissionHelper可以:
- 在获取位置信息前检查必要的权限
- 当权限不足时引导用户授权
- 提供良好的用户体验,避免因权限问题导致的功能异常
- 符合HarmonyOS的安全规范和用户体验标准
更多关于HarmonyOS鸿蒙Next中如何优雅处理权限请求?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中,通过@ohos.abilityAccessCtrl模块处理权限请求。使用requestPermissionsFromUser方法在运行时申请权限,并通过onRequestPermissionsFromUserResult回调处理授权结果。权限需在module.json5文件中声明。
在HarmonyOS Next中,权限请求的优雅处理应遵循“最小化、透明化、用户友好”的原则。核心是通过权限管理模块(securityManager)和声明式UI(ArkTS)实现。
1. 权限声明与检查
- 在
module.json5中精确声明所需权限,仅申请业务必需的权限。 - 使用
abilityAccessCtrl的verifyAccessTokenSync()方法,在权限使用前同步检查授权状态,避免无效请求。
2. 动态请求与用户引导
- 对于敏感权限(如位置、相机),必须使用动态申请。通过
requestPermissionsFromUser()异步请求,并妥善处理回调。 - 在请求前,通过弹窗或界面文字向用户清晰说明权限用途(遵循“权限使用目的”规范),提升通过率。
3. 声明式UI与状态管理
- 利用ArkUI的声明式特性和状态管理(
@State、@Prop),将权限状态与UI组件绑定。例如,权限未授予时禁用相关按钮,授予后自动更新界面。 - 示例代码结构:
[@Entry](/user/Entry) [@Component](/user/Component) struct MyComponent { @State permissionGranted: boolean = false // 检查权限状态 checkPermission() { // ... 调用verifyAccessTokenSync this.permissionGranted = ... } // 请求权限 async requestPermission() { let result = await abilityAccessCtrl.requestPermissionsFromUser(...) this.permissionGranted = ... } build() { Column() { if (this.permissionGranted) { // 授权后的UI Text('已授权') } else { // 未授权时的UI与请求按钮 Button('申请权限').onClick(() => this.requestPermission()) } } } }
4. 处理拒绝与引导设置
- 用户拒绝后,不应重复弹窗骚扰。可记录状态,并在功能入口再次提示,并提供跳转系统设置页的引导(使用
startAbility跳转Settings.ACTION_MANAGE_APP_PERMISSIONS)。 - 通过
onPermissionRejected回调处理拒绝逻辑,解释权限必要性。
5. 后台权限与持续使用
- 对于需要后台持续使用的权限(如后台定位),需单独申请
ACTIVITY_CONTINUOUS类型权限,并在UI中明确告知用户后台使用场景。
总结:HarmonyOS Next的权限处理应融入声明式UI开发流,以状态驱动界面,在关键节点提供清晰用户引导,并尊重用户选择。

