鸿蒙中 应用的权限:申请授权(三)
摘要:本文详细介绍了鸿蒙应用开发中user_grant类型权限的申请流程与实现方法。主要内容包括:权限申请四步流程(声明权限、关联操作、检查授权、处理结果)、核心开发步骤(权限检查与动态申请)、授权结果处理方案,以及权限使用的注意事项(不可持久化状态、弹窗规则等)。通过完整示例展示了如何在UIAbility和页面中实现权限管理,并提供了权限工具类的最佳实践方案,帮助开发者遵循"用户可知可
·
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
鸿蒙应用开发中,当应用需要访问用户的隐私信息或使用系统能力时(如获取位置、使用相机、访问日历等),必须向用户申请授权。这些权限属于user_grant类型。
user_grant权限指的是需要用户明确授权的权限,通常涉及用户隐私信息或系统能力。
常见user_grant权限:
-
位置信息(精确/模糊)
-
相机拍摄
-
麦克风录音
-
日历访问
-
通讯录读取
-
健身运动数据
user_grant权限的申请原则
| 原则 | 说明 |
|---|---|
| 用户可知可控 | 必须由用户主动授权,系统弹窗提示 |
| 最小化原则 | 只申请业务必需的权限 |
| 动态申请 | 在需要使用权限时申请,而非启动时全部申请 |
| 不频繁弹窗 | 避免频繁打扰用户 |
二、user_grant权限申请流程
2.1 四步走流程
步骤1:在配置文件中声明权限
↓
步骤2:将权限与目标操作关联(开发阶段)
↓
步骤3:运行时检查权限 → 未授权则动态申请
↓
步骤4:处理授权结果(同意/拒绝)
2.2 步骤1:声明权限(module.json5)
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:approximately_location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
注意:user_grant权限必须填写reason和usedScene字段,用于上架审核和用户知情。
2.3 步骤2:权限与目标操作关联
在代码层面,将需要权限的操作与权限检查逻辑关联。
三、核心开发步骤(步骤3-4)
3.1 导入所需模块
import { abilityAccessCtrl, bundleManager, Permissions, common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
3.2 检查权限是否已授权
在进行权限申请之前,需要先检查当前应用是否已被授予权限。
// PermissionUtil.ets
import { abilityAccessCtrl, bundleManager, Permissions } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
async function checkPermissionGrant(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
// 1. 获取应用程序的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(`Failed to get bundle info, code: ${err.code}, message: ${err.message}`);
}
// 2. 校验应用是否被授予权限
try {
grantStatus = await atManager.checkAccessToken(tokenId, permission);
} catch (error) {
const err: BusinessError = error as BusinessError;
console.error(`Failed to check access token, code: ${err.code}, message: ${err.message}`);
}
return grantStatus;
}
// 检查多个权限的状态
async function checkPermissions(): Promise<void> {
// 获取精确定位权限状态
let grantStatus1: boolean = await checkPermissionGrant('ohos.permission.LOCATION')
=== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
// 获取模糊定位权限状态
let grantStatus2: boolean = await checkPermissionGrant('ohos.permission.APPROXIMATELY_LOCATION')
=== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
// 精确定位权限只能跟模糊定位权限一起申请,或者已经有模糊定位权限才能申请精确定位权限
if (grantStatus2 && !grantStatus1) {
// 已有模糊权限,需要申请精确定位权限
console.info('Need to request precise location permission');
} else if (!grantStatus1 && !grantStatus2) {
// 两种权限都没有,需要申请模糊定位权限与精确定位权限
console.info('Need to request both permissions');
} else {
// 已经授权,可以继续访问目标操作
console.info('Permissions already granted');
}
}
3.3 动态向用户申请授权
通过requestPermissionsFromUser()方法向用户请求授权。
方式一:在UIAbility中申请授权
// SecondAbility.ets
import { abilityAccessCtrl, common, Permissions, UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
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;
for (let i = 0; i < grantStatus.length; i++) {
if (grantStatus[i] === 0) {
// 用户授权
console.info(`${permissions[i]} is granted by user.`);
} else {
// 用户拒绝授权
console.warn(`${permissions[i]} is denied by user.`);
// 引导用户到设置中开启
this.guideToSettings();
}
}
})
.catch((err: BusinessError) => {
console.error(`Failed to request permissions, code: ${err.code}, message: ${err.message}`);
});
}
export default class SecondAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage): void {
// 重要:需要在loadContent回调中申请权限
windowStage.loadContent('secondpages/Index', (err) => {
if (!err) {
reqPermissionsFromUser(permissions, this.context);
}
});
}
private guideToSettings(): void {
// 引导用户去设置页面
console.info('Guide user to settings');
}
}
方式二:在UI中申请授权
// Index.ets
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();
atManager.requestPermissionsFromUser(context, permissions)
.then((data) => {
let grantStatus: number[] = data.authResults;
for (let i = 0; i < grantStatus.length; i++) {
if (grantStatus[i] === 0) {
console.info(`${permissions[i]} is granted.`);
} else {
console.warn(`${permissions[i]} is denied.`);
// 可以在UI上提示用户
}
}
})
.catch((err: BusinessError) => {
console.error(`Failed to request permissions, code: ${err.code}`);
});
}
@Entry
@Component
struct Index {
aboutToAppear() {
const context: common.UIAbilityContext =
this.getUIContext().getHostContext() as common.UIAbilityContext;
reqPermissionsFromUser(permissions, context);
}
build() {
Column() {
Text('位置权限示例')
.fontSize(20)
.margin(20)
Button('获取位置')
.onClick(() => {
// 每次使用前最好再检查一次权限
// 因为用户可能在设置中关闭了权限
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
3.4 处理授权结果
用户同意授权
if (grantStatus[i] === 0) {
// 用户授权,可以继续访问目标操作
this.accessLocation();
}
用户拒绝授权
当用户拒绝授权时,有几种处理方式:
方式1:引导用户到系统设置中开启
import { common } from '@kit.AbilityKit';
private guideToSettings() {
let context = getContext(this) as common.UIAbilityContext;
context.startAbility({
action: 'action.settings.app.info',
parameters: { bundleName: 'com.example.myapp' }
});
}
方式2:调用requestPermissionOnSetting拉起权限设置弹窗
// 从API 12开始支持
async function requestPermissionInSetting(permission: Permissions) {
let atManager = abilityAccessCtrl.createAtManager();
try {
await atManager.requestPermissionOnSetting(this.context, permission);
// 用户已在设置中授权
console.info('Permission granted in settings');
} catch (error) {
console.error('Failed to request permission in setting');
}
}
四、限制
4.1 弹窗规则
| 规则 | 说明 |
|---|---|
| 不可被遮挡 | 系统权限弹窗不可被其他组件或控件遮挡 |
| 完整展示 | 弹窗信息需完整展示,便于用户识别 |
| 优先级最高 | 如果与其他组件同时展示,系统权限弹窗将默认覆盖其他组件 |
4.2 调用时机限制
| 场景 | 要求 |
|---|---|
| 在UIAbility的onWindowStageCreate中申请 | 需要等待loadContent/setUIContent执行结束后,或在回调中调用 |
| 在UIExtensionAbility中申请 | 需要在onWindowStageCreate函数执行结束后,或在回调中调用 |
| 原因 | 在Content加载完成前,requestPermissionsFromUser会调用失败 |
4.3 权限状态不可持久化
// 错误做法:缓存授权状态
let cachedPermission = true; // 假设用户之前授权了
if (cachedPermission) {
this.accessLocation(); // 可能失败,因为用户可能在设置中关闭了权限
}
// 正确做法:每次使用前检查
async function accessLocation() {
let grantStatus = await checkPermissionGrant('ohos.permission.LOCATION');
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
// 已授权,可以访问
} else {
// 未授权,重新申请
}
}
原因:用户可能在动态授予权限后通过系统设置来取消应用的权限,因此不能将之前授予的授权状态持久化。
五、完整示例
5.1 权限声明(module.json5)
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": "$string:approximately_location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:location_permission_reason",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
}
]
}
}
5.2 权限工具类
// PermissionManager.ets
import { abilityAccessCtrl, bundleManager, Permissions, common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
export class PermissionManager {
private static instance: PermissionManager;
private atManager: abilityAccessCtrl.AtManager;
private constructor() {
this.atManager = abilityAccessCtrl.createAtManager();
}
static getInstance(): PermissionManager {
if (!PermissionManager.instance) {
PermissionManager.instance = new PermissionManager();
}
return PermissionManager.instance;
}
// 检查单个权限
async checkPermission(permission: Permissions): Promise<boolean> {
try {
let bundleInfo = await bundleManager.getBundleInfoForSelf(
bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION
);
let tokenId = bundleInfo.appInfo.accessTokenId;
let grantStatus = await this.atManager.checkAccessToken(tokenId, permission);
return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
} catch (error) {
console.error('Check permission failed', error);
return false;
}
}
// 申请权限
async requestPermissions(permissions: Permissions[], context: common.UIAbilityContext): Promise<boolean> {
try {
let result = await this.atManager.requestPermissionsFromUser(context, permissions);
// 检查是否所有权限都被授予
return result.authResults.every(status => status === 0);
} catch (error) {
console.error('Request permissions failed', error);
return false;
}
}
// 检查并申请权限(一步到位)
async checkAndRequestPermissions(permissions: Permissions[], context: common.UIAbilityContext): Promise<boolean> {
// 先检查是否都已授权
for (let perm of permissions) {
let granted = await this.checkPermission(perm);
if (!granted) {
// 有未授权的权限,发起申请
return await this.requestPermissions(permissions, context);
}
}
return true; // 全部已授权
}
}
5.3 在页面中使用
// LocationPage.ets
import { PermissionManager } from './PermissionManager';
import { common } from '@kit.AbilityKit';
@Entry
@Component
struct LocationPage {
@State hasPermission: boolean = false;
@State locationText: string = '未知位置';
async aboutToAppear() {
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const permissions = [
'ohos.permission.LOCATION',
'ohos.permission.APPROXIMATELY_LOCATION'
];
// 检查并申请权限
this.hasPermission = await PermissionManager.getInstance()
.checkAndRequestPermissions(permissions, context);
}
async getLocation() {
if (!this.hasPermission) {
// 再次尝试申请
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
this.hasPermission = await PermissionManager.getInstance()
.requestPermissions(['ohos.permission.LOCATION'], context);
if (!this.hasPermission) {
// 引导用户去设置
this.showSettingsDialog();
return;
}
}
// 获取位置逻辑
this.locationText = '北京市朝阳区';
}
showSettingsDialog() {
AlertDialog.show({
message: '需要位置权限才能使用此功能,请在设置中开启',
primaryButton: {
value: '去设置',
action: () => {
// 跳转到应用设置页
}
},
secondaryButton: {
value: '取消'
}
});
}
build() {
Column() {
Text(this.locationText)
.fontSize(16)
.margin(20)
Button('获取当前位置')
.enabled(this.hasPermission)
.onClick(() => this.getLocation())
if (!this.hasPermission) {
Text('需要位置权限才能使用此功能')
.fontColor('#FF0000')
.fontSize(14)
.margin(10)
}
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
更多推荐



所有评论(0)