一、前言

在 OpenHarmony 应用开发中,相机、麦克风、定位、文件读写、相册等敏感系统资源,不支持静态直接授权,必须在运行时动态向用户申请。Android、iOS、鸿蒙均遵循「隐私最小权限原则」,而 OpenHarmony 的权限体系更严谨、分层更清晰,分为普通权限、用户授权权限、系统特权权限三类。

很多开发者常见问题:权限已配置却无效、首次拒绝后无法再次弹窗、永久拒绝无引导、多权限批量申请混乱、缺少合规说明导致应用无法上架。

本文基于OpenHarmony 标准系统最新规范,从零讲解:权限配置、权限检测、动态申请、拒绝重试、永久拒绝引导设置、多权限批量封装、企业级工具类封装、隐私合规规范,全部案例可直接上线项目。

二、OpenHarmony权限体系核心原理

2.1 权限分类(官方标准)

  • 普通权限:网络、震动等,只需在module.json5 静态声明,无需弹窗申请,安装即授权。

  • 用户敏感权限(动态权限):相机、麦克风、定位、相册、通讯录,必须运行时弹窗申请,用户可控授权/拒绝。

  • 系统特权权限:仅系统应用或签名应用可用,第三方应用无法申请。

2.2 动态权限完整流程(标准规范)

业务触发 → 检测是否已授权 → 已授权直接执行功能 → 未授权发起系统弹窗 → 判断用户结果(通过/拒绝/永久拒绝)→ 对应业务分支处理。

核心三要素:前置配置、动态检测、结果分支处理(缺一不可)。

三、前置配置:module.json5权限声明

所有需要动态申请的权限,必须在配置文件中声明用途、使用场景,否则无法调起授权弹窗,也是应用上架合规必填项。

路径:src/main/module.json5

"requestPermissions": [
  {
    "name": "ohos.permission.CAMERA",
    "reason": "用于扫码、拍照、图像识别功能",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  },
  {
    "name": "ohos.permission.MICROPHONE",
    "reason": "用于语音录制、语音交互功能",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  },
  {
    "name": "ohos.permission.LOCATION",
    "reason": "用于获取当前位置、附近服务",
    "usedScene": {
      "abilities": ["EntryAbility"],
      "when": "inuse"
    }
  }
]

参数说明: reason:权限用途说明,直接展示给用户,影响用户授权意愿与上架审核; when:inuse:仅前台使用时生效,符合隐私合规要求。

四、核心API详解

动态权限开发仅需两个核心API,所有场景均可覆盖:

  • checkAccessTokenSync:同步检测权限状态(已授权/未授权/永久拒绝)

  • requestPermissionsFromUser:发起系统授权弹窗,批量申请多权限

权限状态常量:

  • PERMISSION_GRANTED(0):已授权

  • PERMISSION_DENIED(-1):未授权、临时拒绝

五、单权限动态申请完整实战(相机为例)

场景:点击拍照按钮,先检测相机权限,无权限自动弹窗,拒绝则提示用户。

import abilityAccessCtrl from '@ohos.abilityAccessCtrl'
import common from '@ohos.app.ability.common'
import promptAction from '@ohos.promptAction'

@Entry
@Component
struct PermissionSingleDemo {
  context = getContext(this) as common.UIAbilityContext
  // 相机权限常量
  private readonly CAMERA_PERM = 'ohos.permission.CAMERA'

  // 检测权限
  async checkCameraPermission(): Promise<boolean> {
    let atManager = abilityAccessCtrl.createAtManager()
    let status = atManager.checkAccessTokenSync(this.context.applicationInfo.accessTokenId, this.CAMERA_PERM)
    return status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
  }

  // 申请相机权限
  async requestCameraPermission() {
    let hasAuth = await this.checkCameraPermission()
    if (hasAuth) {
      promptAction.showToast({ message: "相机已授权,可直接使用" })
      return
    }
    // 发起弹窗申请
    let res = await this.context.requestPermissionsFromUser([this.CAMERA_PERM])
    if (res.authResults[0] === 0) {
      promptAction.showToast({ message: "相机授权成功" })
      // 此处执行拍照、扫码等业务逻辑
    } else {
      promptAction.showToast({ message: "相机权限被拒绝,功能无法使用" })
    }
  }

  build() {
    Column({ space: 20 }) {
      Button("相机权限申请(单权限)")
        .width(240)
        .onClick(() => this.requestCameraPermission())
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

六、多权限批量申请(企业常用)

很多业务需要同时获取相机+麦克风+定位,支持一次性弹窗批量申请,减少用户操作步骤。

// 批量申请 相机+麦克风+定位
async function requestMultiPermission(context: common.UIAbilityContext) {
  const permissions = [
    'ohos.permission.CAMERA',
    'ohos.permission.MICROPHONE',
    'ohos.permission.LOCATION'
  ]
  let res = await context.requestPermissionsFromUser(permissions)
  let allGranted = res.authResults.every(item => item === 0)
  if (allGranted) {
    promptAction.showToast({ message: "所有权限授权成功" })
  } else {
    promptAction.showToast({ message: "部分权限拒绝,功能受限" })
  }
}

七、高阶难点:永久拒绝权限引导跳转设置

用户两次拒绝权限后,系统不再弹窗,普通申请代码完全失效,必须手动引导用户进入系统设置开启权限,这是企业项目必备容错逻辑。

import settings from '@ohos.settings'

// 跳转到应用权限设置页
function jumpPermissionSetting() {
  let uri = 'settings://app/permission?bundleName=' + getContext(this).applicationInfo.bundleName
  settings.openSettings(uri)
}

// 带永久拒绝判断的完整权限申请
async function smartRequestPermission(context: common.UIAbilityContext, perm: string) {
  let atManager = abilityAccessCtrl.createAtManager()
  let tokenId = context.applicationInfo.accessTokenId
  let status = atManager.checkAccessTokenSync(tokenId, perm)

  // 已授权
  if (status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
    return true
  }

  // 发起申请
  let res = await context.requestPermissionsFromUser([perm])
  if (res.authResults[0] === 0) {
    return true
  } else {
    // 拒绝后引导跳转设置
    promptAction.showDialog({
      title: "权限开启提示",
      message: "当前权限已被永久拒绝,请前往设置手动开启",
      primaryButton: {
        text: "去设置",
        action: () => jumpPermissionSetting()
      },
      secondaryButton: { text: "取消" }
    })
    return false
  }
}

八、企业级全局权限工具类(可直接复用)

封装全局工具类,统一管理所有权限检测、申请、跳转逻辑,项目全局调用,解耦业务代码。


import abilityAccessCtrl from '@ohos.abilityAccessCtrl'
import common from '@ohos.app.ability.common'
import promptAction from '@ohos.promptAction'
import settings from '@ohos.settings'

export class PermissionUtil {
  private static context: common.UIAbilityContext

  static initContext(ctx: common.UIAbilityContext) {
    this.context = ctx
  }

  // 检测单权限是否授权
  static async check(perm: string): Promise<boolean> {
    let atMgr = abilityAccessCtrl.createAtManager()
    let status = atMgr.checkAccessTokenSync(this.context.applicationInfo.accessTokenId, perm)
    return status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
  }

  // 智能申请:自动处理已授权、拒绝、永久拒绝
  static async request(perm: string, tip: string): Promise<boolean> {
    if (await this.check(perm)) return true
    let res = await this.context.requestPermissionsFromUser([perm])
    if (res.authResults[0] === 0) {
      promptAction.showToast({ message: `${tip}权限开启成功` })
      return true
    } else {
      await this.showSettingDialog(tip)
      return false
    }
  }

  // 弹出引导设置弹窗
  private static async showSettingDialog(name: string) {
    await promptAction.showDialog({
      title: "权限授权提示",
      message: `功能需要${name}权限支持,请手动开启`,
      primaryButton: {
        text: "前往设置",
        action: () => {
          let uri = `settings://app/permission?bundleName=${this.context.applicationInfo.bundleName}`
          settings.openSettings(uri)
        }
      },
      secondaryButton: { text: "取消" }
    })
  }
}

页面调用示例:

aboutToAppear() {
  PermissionUtil.initContext(getContext(this) as common.UIAbilityContext)
}

// 点击使用相机
async openCamera() {
  let ok = await PermissionUtil.request('ohos.permission.CAMERA', '相机')
  if (ok) {
    // 执行相机业务
  }
}

九、常用动态权限清单(项目常备)

权限用途

权限名称

相机

ohos.permission.CAMERA

麦克风

ohos.permission.MICROPHONE

定位

ohos.permission.LOCATION

相册读取

ohos.permission.READ_IMAGE_VIDEO

文件读写

ohos.permission.READ_WRITE_DOWNLOAD

十、开发规范与隐私合规最佳实践

10.1 权限使用原则

  • 按需申请:禁止初始化一次性申请所有权限,用时再申请

  • 最小权限:不需要的权限绝不声明、不申请

  • 透明告知:reason 描述清晰,明确告知用户用途

10.2 常见踩坑总结

  • 仅写代码不配置 module.json5:弹窗不出现、权限永久失败

  • 用户两次拒绝后不做设置引导:功能彻底失效,体验极差

  • 缺少前置检测,重复频繁弹窗

  • 未做异常分支,拒绝后直接闪退

10.3 上架合规要点

  • 权限用途描述真实、准确、无夸大

  • 非必要权限不声明、不调用

  • 用户拒绝权限后,功能降级可用,不强制卡死应用

十一、全文总结

本文系统性讲解了 OpenHarmony 动态权限的原理、配置、单权限/多权限申请、永久拒绝容错、系统设置跳转、企业级工具封装、隐私合规规范,覆盖开发 99% 的权限场景。

动态权限是应用上架、功能稳定、用户体验的核心基础,也是鸿蒙开发面试、竞赛、项目验收的高频考点。结合前文 UI组件、路由、存储、网络、弹窗、数据库,已构成完整鸿蒙应用开发技术闭环

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐