不懂鸿蒙权限?看这篇就够了(鸿蒙权限获取最佳实践,附完整代码)
这篇鸿蒙权限管理指南以风趣易懂的风格,拆解应用权限合规使用要点。文章先明确最小权限、按需申请等五大核心原则,避免开发者踩 “过度索权” 的坑;再梳理权限分类,将系统授权、用户授权、手动设置授权三类权限的申请门槛与场景讲得清晰明了;还详解 “静态声明 + 动态申请” 的标准流程,针对用户拒绝授权的情况给出解决方案。文末附上可直接复制的权限管理工具类,实现 “先检查权限状态,再分级申请” 的高效逻辑,
鸿蒙权限获取最佳实践
鸿蒙权限管理不踩坑指南:做个“懂分寸”的合规好应用
在鸿蒙(HarmonyOS)的世界里,权限管理就像应用的“社交礼仪”——懂分寸、不越界,才能赢得用户好感和系统“青睐”;要是乱要权限、硬闯隐私,轻则被用户无情卸载,重则过不了应用市场审核。今天就用接地气的吐槽+干货,把鸿蒙权限使用的核心玩法说清楚,让你开发路上少踩雷!
一、鸿蒙权限使用基本原则:做个“不贪心、不霸道”的好应用
鸿蒙对权限的要求,本质就是让应用“守规矩”,这几个原则记牢,能少走99%的弯路:
- 最小权限原则:别当“伸手党”
应用需要啥权限就拿啥,多余的一概不碰!比如一个看小说的APP,非要申请相机、麦克风权限,这不是“没事找事”吗?用户看到直接黑人问号脸,卸载按钮都要按出火星子。 - 必要性原则:按需“点菜”,别一次性“包场”
权限要在用户用到对应功能时再申请,别一打开APP就弹窗“轰炸”:“要位置!要存储!要通讯录!” 这跟刚见面就问人要银行卡密码一样离谱,用户不拒绝你才怪。 - 透明化原则:坦诚相待,别“玩套路”
申请权限前,得跟用户说清楚“要这玩意儿干啥”。比如“要存储权限是为了保存你下载的小说”,别只说“需要存储权限”,用户哪知道你是不是要偷偷存人家照片?坦诚才是必杀技! - 尊重用户选择原则:拒绝就体面点,别死缠烂打
用户拒绝权限后,别反复弹窗“骚扰”,更不能搞“不授权就用不了核心功能”的霸道操作。人家不想给相机权限,你还不让人看小说了?格局打开,给个替代方案不香吗? - 合规性原则:别“走歪路”,系统爸爸会“打屁股”
别想着绕过系统授权、隐藏权限用途这些“骚操作”,鸿蒙的审核机制可不是吃素的,违规的话,应用上架直接“原地凉凉”,前期开发全白费。
二、权限分类:鸿蒙的“权限等级表”,别认错“大佬”和“路人”
鸿蒙把权限分了三六九等,就像职场里的“普通员工”“核心骨干”“大老板”,待遇和申请难度完全不一样,千万别搞混了:
(一)按权限等级分类
- system_grant(系统授权):“路人甲”级别的小透明
不涉及隐私、不影响系统安全,比如网络访问、查看蓝牙的配置这些基础操作。系统会自动“放行”,不用麻烦用户手动授权,只要在配置文件里打个“报告”就行,省心又省力。 - user_grant(用户授权):“高危操作选手”,需用户点头
涉及用户隐私(位置、相册)或核心功能(相机、麦克风)的权限,都是“高危分子”。想使用这些权限,必须让用户明确“点头”同意,系统还会特意弹窗提醒,相当于给用户一个“反悔的机会”。 - manual_settings(手动设置授权):“大佬级”权限,申请门槛拉满
比如拦截键盘输入、无需弹窗录制屏幕、无需弹窗访问用户公共路径这些“核心操作”,属于权限里的“天花板”。想拿到它们可不容易,应用没法直接申请,得引导用户手动去系统设置里开启,相当于要去“大佬办公室”亲自报备。
另外,在system_grant和user_grant类型权限中,还藏着一些特殊的 “受限开放权限”,比如悬浮窗、读取联系人等等。这些权限可不是声明就能用的,必须提前单独申请,否则应用上架时直接会被审核老师打回,连辩解的机会都没有。
受限开放权限申请步骤(直接抄官方流程不踩坑)
(二)按功能权限组分类:权限也爱“抱团取暖”
鸿蒙特别贴心地把功能相关的权限分成了 “小组”,既方便开发者管理,也方便用户理解。这里有个小知识点要划重点:应用请求权限时,同一权限组内的权限会在一个弹窗内统一请求用户授权,用户一旦同意,整个权限组内的权限就会被批量授予。不过有例外 —— 位置信息、通讯录、日历这三个权限组,不遵循这个 “抱团授权” 规则,得单独留意。
以位置信息权限组和相机权限组举个例子帮你快速理解,一看就懂:。
当应用只申请权限ohos.permission.APPROXIMATELY_LOCATION(属于位置信息权限组)时,用户将收到一个请求位置信息的弹窗,包含单个权限的申请。
当应用同时申请权限ohos.permission.APPROXIMATELY_LOCATION和ohos.permission.LOCATION(均属于位置信息权限组)时,用户将收到一个请求位置信息的弹窗,包含两个权限的申请。
当应用同时申请权限ohos.permission.APPROXIMATELY_LOCATION(属于位置信息权限组)和ohos.permission.CAMERA(属于相机权限组)时,用户将收到请求位置信息、请求使用相机的两个弹窗。
权限组完整使用说明(官方清单,按需查阅)
三、权限申请方法:三步搞定,不做“尴尬申请者”
鸿蒙权限申请讲究“先报备、再申请、看结果”,不同权限有不同玩法,一步步来准没错:
(一)静态声明:先给系统“打报告”,不报备可不行
所有权限都得先在项目的 module.json5 文件里“登记备案”,相当于告诉系统“我要用这些权限啦”,这是基础操作,少了这步直接翻车。
- 找到
module.json5文件,在requestPermissions节点里添加权限信息; - 正确示范(别瞎写,权限名称要跟官方一致):
"requestPermissions": [
{
"name": "ohos.permission.CAMERA", // 相机权限,官方名称不能改
"reason": "$string:camera_permission_reason", // 用途说明,比如“拍照上传头像”
"usedScene": {
"abilities": ["MainAbility"], // 哪个功能要用
"when": "inuse" // 只有用的时候才申请,别一直要
}
}
]
- 避坑提醒:用途说明别写“需要权限”这种废话,要写清楚“用权限干嘛”,不然审核老师会给你打回重写!
(二)动态申请:看准时机“表白”,别盲目冲锋
危险权限光报备不够,还得在用户用对应功能时“趁热打铁”申请,流程就像“表白”——先探探口风,再正式出击:
- 先查状态:别做“舔狗”
用PermissionManager查一查权限有没有被授权,已经授权了就直接用,别反复申请,不然用户会烦:“都给你了还问!” - 再发起申请:真诚最重要
没授权就调用requestPermissionsFromUser申请,系统会弹出弹窗,把用途告诉用户,让用户心甘情愿点头。 - 处理结果:成了就用,不成别纠缠
用户同意了就开开心心用功能;拒绝了就好好说:“要开启相机权限才能拍照哦,可去系统设置里打开~” 别逼用户,不然会被反感。 - 代码示例(核心思路,看懂不踩坑):
import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
const permissions: Permissions[] = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'];
function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): void {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
let grantStatus: number[] = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i < length; i++) {
if (grantStatus[i] === 0) {
// 用户授权,可以继续访问目标操作
console.info(`${permissions[i]} is granted by user.`);
} else {
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
return;
}
}
// 授权成功
}).catch((err: BusinessError) => {
console.error(`Failed to request permissions from user, code: ${err.code}, message: ${err.message}`);
})
}
@Entry
@Component
struct Index {
aboutToAppear() {
const context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
reqPermissionsFromUser(permissions, context);
}
build() {
// ...
}
}
(三)用户拒绝授权了怎么办?别慌,有备选方案
这里有个关键知识点:当应用通过requestPermissionsFromUser()拉起弹窗请求用户授权时,如果用户明确拒绝,后续应用就无法再通过这个方法拉起同款弹窗了,只能引导用户去系统设置里手动授权。
在“设置”应用中的路径如下:
路径一:设置 > 隐私与安全 > 权限类型(如位置信息) > 具体应用
路径二:设置 > 应用和元服务 > 某个应用
当然,你也可以更贴心一点,通过调用 requestPermissionOnSetting(),直接拉起权限设置弹窗,帮用户省去手动找路径的麻烦,好感度直接拉满:
import { abilityAccessCtrl, Context, common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
// ···
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let context: Context = this.getUIContext().getHostContext() as common.UIAbilityContext;
atManager.requestPermissionOnSetting(context, ['ohos.permission.APPROXIMATELY_LOCATION']).then((data: Array<abilityAccessCtrl.GrantStatus>) => {
console.info(`requestPermissionOnSetting success, result: ${data}`);
}).catch((err: BusinessError) => {
console.error(`requestPermissionOnSetting fail, code: ${err.code}, message: ${err.message}`);
});
授权最佳实践(直接 ctrl c/ctrl v 就能用,抄作业就完了)
下面给大家整理了一套权限管理工具类,核心逻辑已经帮你梳理清晰,无需理解太深,直接复制到项目里就能用,堪称 “懒人福音”。
工具类核心逻辑说明
- 状态检查阶段:先筛选,再申请,不做无用功
・通过应用tokenId获取真实身份标识,确保权限检查的准确性;
・采用异步方式批量校验每个权限的当前状态,效率更高;
・动态构建待申请权限列表,只处理未授权权限,避免重复弹窗打扰用户。 - 分级申请策略:首次、二次分开处理,更懂用户心理
・首次申请:直接弹窗请求所有未授权权限,一步到位,不折腾用户;
・二次申请:当检测到dialogShownResults存在false值时(说明用户已拒绝过一次)→
自动转入设置页引导模式,不反复弹窗惹人烦;→ 逐个发起权限申请(因系统限制,二次申请不同权限组无法批量操作),提高授权成功率。
具体调用方式(注意:权限需要先在module.json5中声明)
checkAndRequestPermissions(['ohos.permission.MICROPHONE', 'ohos.permission.CAMERA'], "hello world")
权限管理工具类(完整可复制)
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';
import bundleManager from '@ohos.bundle.bundleManager';
import common from '@ohos.app.ability.common';
import promptAction from '@ohos.promptAction';
import { BusinessError } from '@ohos.base';
/**
* 单个权限授权状态检查
* @param permission 待检查的单个权限
* @returns 权限授权状态
*/
async function checkPermissionGrant(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
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(`获取应用Bundle信息失败,错误码:${err.code},错误信息:${err.message}`);
return grantStatus;
}
// 校验应用是否被授予该权限
try {
grantStatus = await atManager.checkAccessToken(tokenId, permission);
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`检查权限授权状态失败,错误码:${err.code},错误信息:${err.message}`);
}
return grantStatus;
}
/**
* 权限申请辅助方法(处理首次拒绝与二次引导设置)
* @param permissions 待申请的权限数组
* @param refuseStr 权限申请被拒绝后的提示语
* @returns 最终是否授权成功
*/
async function requestPermissionHelper(permissions: Array<Permissions>, refuseStr: string): Promise<boolean> {
// 判断首次申请是否全部授权成功
let userGrant = true;
try {
let context = getContext() as common.UIAbilityContext;
let isFirstTime: boolean = true;
let atManager = abilityAccessCtrl.createAtManager();
let grantStatus = await atManager.requestPermissionsFromUser(context, permissions);
for (let element of grantStatus.authResults) {
if (element !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
userGrant = false;
break;
}
}
// 判断是否为非首次申请(弹窗未显示说明已被用户拒绝过一次)
if (grantStatus.dialogShownResults) {
for (let element of grantStatus.dialogShownResults) {
if (!element) {
isFirstTime = false;
break;
}
}
}
// 非首次申请且授权失败:引导用户去设置页面开启
if (!isFirstTime && !userGrant) {
// 不同权限组不能同时申请,逐个申请并校验
for (let permission of permissions) {
const data: Array<abilityAccessCtrl.GrantStatus> =
await atManager.requestPermissionOnSetting(context, [permission]);
userGrant = true;
for (let element of data) {
if (element !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
userGrant = false;
}
}
if (!userGrant) {
promptAction.showToast({
message: refuseStr,
duration: 5000
});
}
}
} else if (isFirstTime && !userGrant) {
// 首次申请且授权失败:仅提示用户
promptAction.showToast({
message: refuseStr,
duration: 5000
});
}
}catch ( err){
console.error(`Request permissions failed, code: ${(err as BusinessError).code}, message: ${(err as BusinessError).message}`);
userGrant = false;
}
return userGrant;
}
/**
* 核心封装函数:先批量检查所有权限,仅对未授权权限发起申请
* @param permissions 待校验/申请的权限数组
* @param refuseStr 权限申请被拒绝后的提示语
* @returns 所有权限是否最终授权成功(true:全部授权;false:存在未授权权限)
*/
export async function checkAndRequestPermissions(permissions: Array<Permissions>, refuseStr: string): Promise<boolean> {
// 边界处理:空权限数组直接返回授权成功
if (!permissions || permissions.length === 0) {
console.warn("待申请权限数组为空,无需处理");
return true;
}
// 第一步:批量检查所有权限的授权状态,筛选出未授权的权限
const unGrantedPermissions: Array<Permissions> = [];
for (const permission of permissions) {
const grantStatus = await checkPermissionGrant(permission);
if (grantStatus !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
unGrantedPermissions.push(permission);
console.log(`权限【${permission}】未授权,加入申请队列`);
} else {
console.log(`权限【${permission}】已授权,无需重复申请`);
}
}
// 第二步:如果所有权限都已授权,直接返回true
if (unGrantedPermissions.length === 0) {
console.log("所有权限均已授权,无需发起申请");
return true;
}
// 第三步:仅对未授权权限发起申请,返回最终申请结果
console.log(`开始发起未授权权限申请,待申请权限:${unGrantedPermissions.join(', ')}`);
const finalGrantResult = await requestPermissionHelper(unGrantedPermissions, refuseStr);
return finalGrantResult;
}
一、核心调用流程
-
入口方法:checkAndRequestPermissions(permissions, refuseStr)
└─▶ 边界检查:空数组直接返回授权成功
└─▶ 执行权限状态检查(checkPermissionGrant)
└─▶ 通过bundleManager获取应用tokenId
└─▶ 调用AtManager.checkAccessToken验证权限状态
└─▶ 筛选出未授权权限集合unGrantedPermissions
└─▶ 调用requestPermissionHelper发起实际授权请求 -
权限申请辅助方法:requestPermissionHelper(unGrantedPermissions, refuseStr)
└─▶ 首次申请请求
└─▶ 通过AtManager.requestPermissionsFromUser发起弹窗申请
└─▶ 校验授权结果authResults
└─▶ 二次申请处理
└─▶ 通过dialogShownResults判断是否首次拒绝
└─▶ 调用requestPermissionOnSetting引导系统设置页
└─▶ 逐个权限进行二次申请校验
结语
其实鸿蒙权限管理并没有想象中复杂,核心就是 “懂分寸、不贪心、够坦诚”。遵循本文的原则、分类标准和申请方法,再直接抄用现成的权限管理工具类,你的应用既能顺利实现功能,又能让用户觉得 “这 APP 真懂事”,好感度拉满!
如果想了解更详细的权限列表、API 用法,可直接查阅华为开发者联盟官方文档,合规开发才是在鸿蒙生态里走得更远的关键哦!
更多推荐




所有评论(0)