在鸿蒙(HarmonyOS)原生开发中,相机、相册、位置等运行时动态敏感权限的申请逻辑十分繁琐。开发者不仅需要处理“检查权限 -> 发起申请 -> 处理回调”的基础流程,还要应对“永久拒绝后引导跳转系统设置”、“多权限并发回调错乱”以及“页面销毁时弹窗残留”等痛点。

为了解决这些问题,鸿蒙生态中涌现了多款优秀的权限请求库,它们通过链式调用、统一状态管理和二次弹窗引导等机制,极大地简化了开发者的工作。

一、 主流权限请求库概览

  1. HMPermission:一款采用链式调用方式的权限请求框架。它封装了权限请求逻辑,支持批量申请权限,并在内部主动校验权限是否已授权,提供了授权成功与拒绝的清晰回调。
  2. @pura/harmony-utils (PermissionUtil):鸿蒙开发中非常受欢迎的第三方工具库。其 PermissionUtil 提供了从权限校验到二次引导的完整链路,特别是 requestPermissionsEasy 方法,能在用户拒绝后自动进行二次申请授权。
  3. @nutpi/simple_permission:专注于鸿蒙应用开发的开源工具库,提供了 PermissionManager 类,支持权限检查、申请以及拉起权限设置页面等核心功能,API 设计简洁。
  4. 桃夭 (TaoYao):另一款采用链式调用的权限请求框架,其最大亮点是支持在 UIUIAbility 和 UIExtensionAbility 中灵活申请权限,通过策略模式自动获取对应的 Context 对象。
1. HMPermission:链式调用与回调处理

场景:使用链式 API 优雅地申请相机权限,并分别处理授权成功与拒绝的逻辑。

import { HMPermission } from '@sy/hmpermission';
import { common, Permissions } from '@kit.AbilityKit';

private permissions: Array<Permissions> = ['ohos.permission.CAMERA'];
private context: common.UIAbilityContext = getContext() as common.UIAbilityContext;

// 发起权限申请
HMPermission
  .with(this.context)
  .permission(this.permissions)
  .onGranted(() => {
    console.info('相机权限授权成功,准备打开相机');
  })
  .onDenied((deniedArr: Array<Permissions>) => {
    console.warn('相机权限被拒绝: ' + JSON.stringify(deniedArr));
    // 引导用户前往系统设置
    HMPermission.openSystemSettings(this.context);
  })
  .request();
2. @pura/harmony-utils:一键申请与二次引导

场景:利用 requestPermissionsEasy 方法处理定位权限。该方法封装了完整的授权链路,若用户首次拒绝,会自动触发二次申请引导。

import { PermissionUtil } from '@pura/harmony-utils';

// 申请精确定位与模糊定位权限
PermissionUtil.requestPermissionsEasy([
  'ohos.permission.LOCATION', 
  'ohos.permission.APPROXIMATELY_LOCATION'
]).then((isGranted) => {
  if (isGranted) {
    console.info('定位权限已获取,开始获取当前位置');
  } else {
    console.warn('用户最终拒绝了定位权限');
  }
});
3. @nutpi/simple_permission:简洁的异步请求

场景:使用 PermissionManager 发起异步权限请求,获取明确的布尔值结果。

import { PermissionManager } from '@nutpi/simple_permission';
import { Permissions } from '@kit.AbilityKit';

// 异步检查并申请麦克风权限
const isGranted: boolean = await PermissionManager.requestPermission([
  'ohos.permission.MICROPHONE' as Permissions
]);

if (isGranted) {
  console.info('麦克风权限申请成功');
} else {
  console.warn('麦克风权限申请失败');
}
4. 桃夭 (TaoYao):多场景 Context 适配

场景:在 UI 组件中直接发起权限申请,TaoYao 会通过策略模式自动获取对应的 Context 对象,无需手动传递。

import { TaoYao } from '@shijing/taoyao/Index';
import { Permissions } from '@kit.AbilityKit';

// 在 UI 组件中直接调用,支持链式操作
TaoYao.with(this)
  .runtime()
  .permission(['ohos.permission.CAMERA'] as Array<Permissions>)
  .onGranted(() => {
    console.info('桃夭框架:相机权限申请成功');
  })
  .onDenied(() => {
    console.warn('桃夭框架:相机权限被拒绝');
  })
  .request();

二、 核心封装能力与最佳实践

优秀的权限库通常具备以下核心能力:

  • 前置校验与按需请求:在申请前自动校验权限状态,避免重复弹窗。同时遵循最小权限原则,在用户触发具体功能(如点击“获取当前位置”)时再发起申请。
  • 拒绝后的优雅降级与引导:当用户拒绝权限甚至勾选“不再询问”时,不强制弹窗打扰,而是通过自定义 UI 提示,并提供一键跳转系统设置页的能力,引导用户手动开启。
  • 明确的权限声明原因:在 module.json5 中规范填写 reason 字段,向用户清晰说明为何需要该权限,以提升授权通过率。

三、 典型场景实战代码

以下展示两款主流库在真实业务中的代码实现:

场景 1:使用 HMPermission 链式申请相机权限

import { HMPermission, Permissions } from '@sy/hmpermission';
import { common } from '@kit.AbilityKit';

private permissions: Array<Permissions> = ['ohos.permission.CAMERA'];
private context: common.UIAbilityContext = getContext() as common.UIAbilityContext;

// 链式调用,代码极其简洁
HMPermission
  .with(this.context)
  .permission(this.permissions)
  .onGranted(() => {
    console.info('相机权限授权成功,开始拍照');
  })
  .onDenied((deniedArr: Array<Permissions>) => {
    console.warn('相机权限被拒绝: ' + JSON.stringify(deniedArr));
    // 可在此处引导用户前往系统设置
    // HMPermission.openSystemSettings(this.context);
  })
  .request();

场景 2:使用 @pura/harmony-utils 处理定位权限(含二次引导)

import { PermissionUtil } from '@pura/harmony-utils';

// 推荐使用 requestPermissionsEasy,拒绝后自动二次向用户申请授权
PermissionUtil.requestPermissionsEasy(
  ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION']
).then((isGranted) => {
  if (isGranted) {
    console.info('定位权限已获取');
  } else {
    console.warn('用户最终拒绝了定位权限');
  }
});
  1. 优先使用系统 Picker 或安全控件:如果仅仅是为了读取媒体库图片或拉起系统相机拍照,优先使用 PhotoViewPicker 或 CameraPicker。它们依赖系统独立进程,无需应用额外申请权限即可临时受限访问资源。
  2. 配置规范不可遗漏:对于 user_grant 类型的权限,必须在 module.json5 中完整填写 namereason 和 usedScene,否则会导致审核不通过或运行崩溃。
  3. 调用敏感 API 前必检查:任何涉及敏感权限的 API 调用前,务必先调用 checkPermissions 或 hasPermission 验证状态,避免引发安全异常。
  4. 尊重用户选择:如果用户拒绝了权限,应用不应在同一个操作上下文中再次弹窗强制请求。应在页面适当位置添加提示,直到用户重新触发该功能时再引导授权。

四、 原生 API 封装实战:打造极简权限工具类

场景:当项目不想引入第三方库时,可基于鸿蒙原生 @kit.AbilityKit 封装一个轻量级的权限工具类,实现“先检查、再申请”的标准化流程,避免每次调用都写冗长的样板代码。

import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';

export class PermissionUtil {
    // 1. 检查并申请权限(合并操作)
    static async checkAndRequest(context: Context, permission: Permissions): Promise<boolean> {
        const atManager = abilityAccessCtrl.createAtManager();
        
        // 先检查当前权限状态
        const status = await atManager.checkAccessToken(context.tokenId, permission);
        if (status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
            return true; // 已授权,直接返回
        }
        
        // 未授权,发起动态申请弹窗
        const result = await atManager.requestPermissionsFromUser(context, [permission]);
        return result.authResults[0] === 0; // 0 表示授权成功
    }
}

五、 二次授权引导实战:优雅处理用户拒绝

场景:当用户在系统弹窗中点击“拒绝”甚至勾选了“不再询问”时,应用需通过自定义弹窗提供“去设置”的入口,引导用户手动开启权限,避免功能静默失效。

import { promptAction } from '@kit.ArkUI';

// 结合上面的 PermissionUtil 使用
async function safeOpenCamera(context: Context) {
    const isGranted = await PermissionUtil.checkAndRequest(context, 'ohos.permission.CAMERA');
    
    if (isGranted) {
        console.info('权限已获取,打开相机');
    } else {
        // 用户拒绝后,弹出二次引导对话框
        const dialogResult = await promptAction.showDialog({
            title: "温馨提示",
            message: "未授权相机权限将无法拍照,是否前往设置开启?",
            buttons: [
                { text: '取消', color: '#999999' },
                { text: '去设置', color: '#0A59F7' }
            ]
        });
        
        // 用户点击“去设置”,拉起系统应用详情页
        if (dialogResult.index === 1) {
            const atManager = abilityAccessCtrl.createAtManager();
            await atManager.requestPermissionOnSetting(context, ['ohos.permission.CAMERA']);
        }
    }
}

六、 全局上下文注入实战:摆脱 Context 传递烦恼

场景:在深层嵌套的组件中获取 Context 较为繁琐。通过在应用启动时将 UIAbilityContext 存入全局状态,权限工具类即可在任何地方直接调用,无需层层传参。

// 1. 在 EntryAbility.ets 的 onWindowStageCreate 中存储
AppStorage.setOrCreate('appContext', this.context);

// 2. 在 PermissionUtil 中自动获取 Context
static async checkAndRequest(permission: Permissions): Promise<boolean> {
    const context = AppStorage.get<Context>('appContext');
    if (!context) return false;
    
    const atManager = abilityAccessCtrl.createAtManager();
    // ... 后续检查与申请逻辑
}

在落地权限请求时,开发者需特别注意以下合规与工程陷阱:

  1. 严禁启动时连环弹窗:绝不能在应用刚启动(如 onWindowStageCreate)时集中请求所有权限。必须在用户真正触发相关功能(如点击“扫一扫”)时再按需请求,否则极易被应用市场判定为“不合理提示”而拒审。
  2. 用途说明必须清晰:在 module.json5 中声明 user_grant 权限时,reason 字段必须清晰解释“为什么需要该权限”以及“拒绝后会影响什么功能”,避免使用“为了提供更好的服务”等模糊话术。
  3. 第三方库权限背锅:引入第三方 HAR/HSP 依赖时,务必检查其内部是否声明了敏感权限。若应用本身不需要该功能,需在打包时剔除或在审核时提供详细说明,避免因第三方库导致审核失败。
  4. 全局开关校验:对于定位、蓝牙等功能,除了应用内权限,还需检查系统级的全局开关是否开启,可通过 PermissionUtil.requestGlobalSwitch 引导用户打开系统总开关。
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐