Skip to content

运行时权限申请

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: 运行时权限申请的步骤?

  1. module.json5 中声明权限
  2. 调用 verifyAccessToken 检查是否已授权
  3. 未授权则调用 requestPermissionsFromUser 申请
  4. 根据 authResults 处理用户授权结果

Q2: 如何判断权限被永久拒绝?

:鸿蒙没有直接 API 判断"不再询问"。通常做法是:多次申请都被拒后,引导用户到设置页面手动开启。

Q3: 批量申请权限时如何处理部分授权?

:遍历 authResults 数组,对每个权限的授权结果分别处理。已授权的正常使用,被拒绝的提示或降级。


7. 与 Android 对照

概念HarmonyOSAndroid
权限检查verifyAccessTokencheckSelfPermission
权限申请requestPermissionsFromUserrequestPermissions
结果回调Promise authResultsonRequestPermissionsResult
TokenIDTokenIDUID
永久拒绝无直接 APIshouldShowRequestPermissionRationale

面试提示:运行时权限申请是开发必备技能,需掌握完整的检查→申请→处理流程。