Appearance
运行时权限申请
user_granted 等级的权限需要在运行时动态向用户申请。
1. 权限申请流程
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ 检查权限 │───▶│ 未授权? │───▶│ 申请权限 │
│ │ │ │ │ │
│ checkAccess │ │ 是 → 申请 │ │ requestPerm │
│ Token │ │ 否 → 使用 │ │ FromUser │
└─────────────┘ └──────────────┘ └─────────────┘
│
▼
┌──────────────┐
│ 用户响应 │
│ │
│ 同意/拒绝 │
└──────────────┘2. 权限检查 API
异步检查
typescript
import { accessControl } from '@kit.ArkKit';
import { bundleManager } from '@kit.ArkKit';
async function checkCameraPermission(): Promise<boolean> {
// 获取应用 TokenID
let bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
);
let tokenId = bundleInfo.tokenId;
// 检查权限
let result = await accessControl.verifyAccessToken({
permissionName: 'ohos.permission.CAMERA',
tokenId: tokenId
});
return result === 0; // 0 表示已授权
}
// 使用
let hasPermission = await checkCameraPermission();
if (hasPermission) {
// 可以使用相机
openCamera();
} else {
// 需要申请权限
requestCameraPermission();
}同步检查
typescript
import { accessControl } from '@kit.ArkKit';
function checkPermissionSync(permissionName: string): boolean {
let tokenId = getTokenIdSync();
let result = accessControl.verifyAccessTokenSync({
permissionName: permissionName,
tokenId: tokenId
});
return result === 0;
}3. 权限申请 API
基本申请
typescript
import { accessControl } from '@kit.ArkKit';
async function requestCameraPermission(): Promise<void> {
try {
let result = await accessControl.requestPermissionsFromUser({
permissions: ['ohos.permission.CAMERA']
});
if (result.authResults[0] === 0) {
console.log('相机权限已授予');
// 可以打开相机
openCamera();
} else {
console.log('相机权限被拒绝');
// 提示用户或降级处理
showPermissionDeniedTip();
}
} catch (error) {
console.error('权限申请失败:', error);
}
}批量申请
typescript
async function requestMultiplePermissions(): Promise<void> {
let permissions = [
'ohos.permission.CAMERA',
'ohos.permission.MICROPHONE',
'ohos.permission.READ_MEDIA'
];
let result = await accessControl.requestPermissionsFromUser({
permissions: permissions
});
// 处理每个权限的结果
result.authResults.forEach((authResult, index) => {
let permission = permissions[index];
if (authResult === 0) {
console.log(`${permission} 已授予`);
} else {
console.log(`${permission} 被拒绝`);
}
});
}4. 完整的权限管理工具类
typescript
// utils/PermissionUtil.ts
import { accessControl } from '@kit.ArkKit';
import { bundleManager } from '@kit.ArkKit';
export class PermissionUtil {
private static tokenId: string = '';
// 初始化 TokenID
static async initTokenId(): Promise<void> {
let bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_DEFAULT
);
this.tokenId = bundleInfo.tokenId;
}
// 检查单个权限
static async checkPermission(permissionName: string): Promise<boolean> {
if (!this.tokenId) {
await this.initTokenId();
}
let result = await accessControl.verifyAccessToken({
permissionName: permissionName,
tokenId: this.tokenId
});
return result === 0;
}
// 申请单个权限
static async requestPermission(permissionName: string): Promise<boolean> {
let result = await accessControl.requestPermissionsFromUser({
permissions: [permissionName]
});
return result.authResults[0] === 0;
}
// 检查并申请(如果未授权)
static async checkAndRequest(permissionName: string): Promise<boolean> {
let hasPermission = await this.checkPermission(permissionName);
if (!hasPermission) {
hasPermission = await this.requestPermission(permissionName);
}
return hasPermission;
}
// 批量检查
static async checkPermissions(permissionNames: string[]): Promise<boolean> {
for (let perm of permissionNames) {
if (!await this.checkPermission(perm)) {
return false;
}
}
return true;
}
// 批量申请
static async requestPermissions(permissionNames: string[]): Promise<number[]> {
let result = await accessControl.requestPermissionsFromUser({
permissions: permissionNames
});
return result.authResults;
}
}
// 使用示例
async function useCamera() {
let granted = await PermissionUtil.checkAndRequest('ohos.permission.CAMERA');
if (granted) {
openCamera();
} else {
showToast('相机权限被拒绝,无法使用此功能');
}
}5. 权限被拒处理
永久拒绝处理
typescript
async function handlePermissionDenied(permissionName: string): Promise<void> {
// 检查是否是"不再询问"的永久拒绝
// 鸿蒙没有直接的 API 判断,需要通过用户体验推断
// 策略:引导用户到设置页面手动开启
showPermissionDialog({
title: '权限被拒绝',
message: '请在系统设置中手动开启此权限',
confirmText: '去设置',
cancelText: '取消',
onConfirm: () => {
// 跳转到应用设置页面
openAppSettings();
}
});
}
function openAppSettings(): void {
// 使用 Want 跳转到设置
let want: Want = {
action: 'action.settings.APP_SETTINGS',
bundleName: 'com.ohos.settings'
};
context.startAbility(want);
}优雅降级
typescript
async function useLocationWithFallback(): Promise<void> {
let hasLocation = await PermissionUtil.checkAndRequest('ohos.permission.LOCATION');
if (hasLocation) {
// 使用 GPS 定位
getCurrentLocation();
} else {
// 降级方案:使用 IP 定位或让用户手动选择
getLocationByIP();
showManualLocationPicker();
}
}6. 面试高频问题
Q1: 运行时权限申请的步骤?
答:
- 在
module.json5中声明权限 - 调用
verifyAccessToken检查是否已授权 - 未授权则调用
requestPermissionsFromUser申请 - 根据
authResults处理用户授权结果
Q2: 如何判断权限被永久拒绝?
答:鸿蒙没有直接 API 判断"不再询问"。通常做法是:多次申请都被拒后,引导用户到设置页面手动开启。
Q3: 批量申请权限时如何处理部分授权?
答:遍历 authResults 数组,对每个权限的授权结果分别处理。已授权的正常使用,被拒绝的提示或降级。
7. 与 Android 对照
| 概念 | HarmonyOS | Android |
|---|---|---|
| 权限检查 | verifyAccessToken | checkSelfPermission |
| 权限申请 | requestPermissionsFromUser | requestPermissions |
| 结果回调 | Promise authResults | onRequestPermissionsResult |
| TokenID | TokenID | UID |
| 永久拒绝 | 无直接 API | shouldShowRequestPermissionRationale |
面试提示:运行时权限申请是开发必备技能,需掌握完整的检查→申请→处理流程。