权限管理
HarmonyOS 采用分级权限管理机制,权限分为 system_grant(系统授权)和 user_grant(用户授权)两种类型。应用需要在 module.json5 中声明权限,敏感权限还需在运行时动态申请。
权限类型
| 类型 | 说明 | 申请方式 |
|---|---|---|
| system_grant | 系统授权权限 | 在 module.json5 中声明即可,安装时自动授予 |
| user_grant | 用户授权权限 | 需在 module.json5 声明,并在运行时动态申请 |
前置配置
module.json5 权限声明
在 module.json5 的 requestPermissions 数组中声明应用需要的权限:
json5
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "$string:reason_internet",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
},
{
"name": "ohos.permission.CAMERA",
"reason": "$string:reason_camera",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:reason_location",
"usedScene": {
"abilities": ["EntryAbility"],
"when": "always"
}
}
]
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
字段说明
| 字段 | 必填 | 说明 |
|---|---|---|
name | 是 | 权限名称 |
reason | 否 | 权限申请理由,建议引用字符串资源 |
usedScene.abilities | 否 | 需要使用该权限的 Ability 列表 |
usedScene.when | 否 | 使用时机:inuse(使用时)/ always(始终) |
字符串资源
在 resources/base/element/string.json 中定义权限理由:
json
{
"string": [
{
"name": "reason_internet",
"value": "用于访问网络数据"
},
{
"name": "reason_camera",
"value": "用于拍照和扫描二维码"
},
{
"name": "reason_location",
"value": "用于获取当前位置信息"
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
API 列表
| API | 说明 | 起始版本 |
|---|---|---|
abilityAccessCtrl.createAtManager() | 创建权限管理器 | API 9 |
AtManager.requestPermissionsFromUser() | 向用户申请权限 | API 9 |
AtManager.checkAccessToken() | 检查权限授权状态 | API 9 |
AtManager.requestPermissionOnSetting() | 跳转设置页申请权限 | API 9 |
常用权限清单
网络相关
| 权限 | 类型 | 说明 |
|---|---|---|
ohos.permission.INTERNET | system_grant | 访问网络 |
ohos.permission.GET_NETWORK_INFO | system_grant | 获取网络信息 |
定位相关
| 权限 | 类型 | 说明 |
|---|---|---|
ohos.permission.APPROXIMATELY_LOCATION | user_grant | 获取大概位置 |
ohos.permission.LOCATION | user_grant | 获取精确位置 |
相机与媒体
| 权限 | 类型 | 说明 |
|---|---|---|
ohos.permission.CAMERA | user_grant | 使用相机 |
ohos.permission.MICROPHONE | user_grant | 使用麦克风 |
ohos.permission.READ_IMAGEVIDEO | user_grant | 读取图片/视频 |
ohos.permission.WRITE_IMAGEVIDEO | user_grant | 写入图片/视频 |
存储相关
| 权限 | 类型 | 说明 |
|---|---|---|
ohos.permission.READ_MEDIA | user_grant | 读取媒体文件 |
ohos.permission.WRITE_MEDIA | user_grant | 写入媒体文件 |
系统相关
| 权限 | 类型 | 说明 |
|---|---|---|
ohos.permission.NOTIFICATION | user_grant | 发送通知 |
ohos.permission.KEEP_BACKGROUND_RUNNING | user_grant | 后台运行 |
权限状态检查
检查单个权限
typescript
import { abilityAccessCtrl } from '@kit.AbilityKit'
function checkPermission(permission: string): boolean {
const atManager = abilityAccessCtrl.createAtManager()
const tokenId = getContext().applicationInfo.accessTokenId
try {
const result = atManager.checkAccessToken(tokenId, permission)
return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
} catch (error) {
console.error('检查权限失败:', JSON.stringify(error))
return false
}
}
// 使用示例
const hasCamera = checkPermission('ohos.permission.CAMERA')
console.info('相机权限:', hasCamera ? '已授权' : '未授权')1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
检查多个权限
typescript
import { abilityAccessCtrl } from '@kit.AbilityKit'
interface PermissionCheckResult {
permission: string
granted: boolean
}
function checkPermissions(permissions: string[]): PermissionCheckResult[] {
const atManager = abilityAccessCtrl.createAtManager()
const tokenId = getContext().applicationInfo.accessTokenId
return permissions.map(permission => {
try {
const result = atManager.checkAccessToken(tokenId, permission)
return {
permission,
granted: result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
}
} catch (error) {
return { permission, granted: false }
}
})
}
// 使用示例
const results = checkPermissions([
'ohos.permission.CAMERA',
'ohos.permission.LOCATION',
'ohos.permission.MICROPHONE'
])
results.forEach(({ permission, granted }) => {
console.info(`${permission}: ${granted ? '已授权' : '未授权'}`)
})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
动态申请权限
申请单个权限
typescript
import { abilityAccessCtrl } from '@kit.AbilityKit'
async function requestPermission(permission: string): Promise<boolean> {
const atManager = abilityAccessCtrl.createAtManager()
try {
const result = await atManager.requestPermissionsFromUser(
getContext(),
[permission]
)
return result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
} catch (error) {
console.error('申请权限失败:', JSON.stringify(error))
return false
}
}
// 使用示例
async function useCamera() {
const granted = await requestPermission('ohos.permission.CAMERA')
if (granted) {
console.info('相机权限已获取,可以使用相机')
} else {
console.info('相机权限被拒绝')
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
申请多个权限
typescript
import { abilityAccessCtrl, PermissionRequestResult } from '@kit.AbilityKit'
interface PermissionRequest {
permission: string
granted: boolean
}
async function requestMultiplePermissions(permissions: string[]): Promise<PermissionRequest[]> {
const atManager = abilityAccessCtrl.createAtManager()
try {
const result: PermissionRequestResult = await atManager.requestPermissionsFromUser(
getContext(),
permissions
)
return result.permissions.map((permission, index) => ({
permission,
granted: result.authResults[index] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
}))
} catch (error) {
console.error('申请权限失败:', JSON.stringify(error))
return permissions.map(permission => ({ permission, granted: false }))
}
}
// 使用示例
async function initApp() {
const results = await requestMultiplePermissions([
'ohos.permission.CAMERA',
'ohos.permission.LOCATION',
'ohos.permission.MICROPHONE'
])
results.forEach(({ permission, granted }) => {
console.info(`${permission}: ${granted ? '已授权' : '未授权'}`)
})
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
完整权限管理封装
typescript
import { abilityAccessCtrl, PermissionRequestResult } from '@kit.AbilityKit'
import { promptAction } from '@kit.ArkUI'
interface PermissionConfig {
permission: string
reason: string
required: boolean // 是否为必需权限
}
class PermissionManager {
private static instance: PermissionManager
static getInstance(): PermissionManager {
if (!PermissionManager.instance) {
PermissionManager.instance = new PermissionManager()
}
return PermissionManager.instance
}
// 检查权限是否已授权
checkPermission(permission: string): boolean {
const atManager = abilityAccessCtrl.createAtManager()
const tokenId = getContext().applicationInfo.accessTokenId
try {
const result = atManager.checkAccessToken(tokenId, permission)
return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
} catch {
return false
}
}
// 检查多个权限
checkPermissions(permissions: string[]): Record<string, boolean> {
const results: Record<string, boolean> = {}
permissions.forEach(permission => {
results[permission] = this.checkPermission(permission)
})
return results
}
// 申请单个权限
async requestPermission(permission: string): Promise<boolean> {
if (this.checkPermission(permission)) {
return true
}
const atManager = abilityAccessCtrl.createAtManager()
try {
const result = await atManager.requestPermissionsFromUser(getContext(), [permission])
return result.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
} catch {
return false
}
}
// 申请多个权限
async requestPermissions(permissions: string[]): Promise<Record<string, boolean>> {
const atManager = abilityAccessCtrl.createAtManager()
const results: Record<string, boolean> = {}
// 先检查哪些权限未授权
const uncheckedPermissions = permissions.filter(p => !this.checkPermission(p))
if (uncheckedPermissions.length === 0) {
permissions.forEach(p => results[p] = true)
return results
}
try {
const result: PermissionRequestResult = await atManager.requestPermissionsFromUser(
getContext(),
uncheckedPermissions
)
uncheckedPermissions.forEach((permission, index) => {
results[permission] = result.authResults[index] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED
})
// 已授权的权限
permissions.forEach(permission => {
if (!uncheckedPermissions.includes(permission)) {
results[permission] = true
}
})
} catch {
permissions.forEach(permission => {
results[permission] = this.checkPermission(permission)
})
}
return results
}
// 申请必需权限(带提示)
async requestRequiredPermissions(configs: PermissionConfig[]): Promise<boolean> {
const permissions = configs.map(c => c.permission)
const results = await this.requestPermissions(permissions)
const deniedRequired = configs.filter(
config => config.required && !results[config.permission]
)
if (deniedRequired.length > 0) {
const names = deniedRequired.map(c => c.reason).join('、')
promptAction.showToast({
message: `需要授权 ${names} 才能继续使用`,
duration: 3000
})
return false
}
return true
}
// 跳转设置页
async openPermissionSettings(): Promise<void> {
const atManager = abilityAccessCtrl.createAtManager()
try {
await atManager.requestPermissionOnSetting(getContext(), [])
} catch (error) {
console.error('跳转设置页失败:', JSON.stringify(error))
}
}
}
export const permissionManager = PermissionManager.getInstance()1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
使用示例
typescript
import { permissionManager } from '../utils/PermissionManager'
// 定义应用需要的权限
const APP_PERMISSIONS = [
{ permission: 'ohos.permission.CAMERA', reason: '相机', required: true },
{ permission: 'ohos.permission.LOCATION', reason: '定位', required: false },
{ permission: 'ohos.permission.MICROPHONE', reason: '麦克风', required: false }
]
@Entry
@Component
struct PermissionDemo {
@State cameraGranted: boolean = false
@State locationGranted: boolean = false
@State microphoneGranted: boolean = false
aboutToAppear() {
this.checkPermissions()
}
checkPermissions() {
this.cameraGranted = permissionManager.checkPermission('ohos.permission.CAMERA')
this.locationGranted = permissionManager.checkPermission('ohos.permission.LOCATION')
this.microphoneGranted = permissionManager.checkPermission('ohos.permission.MICROPHONE')
}
async requestCamera() {
const granted = await permissionManager.requestPermission('ohos.permission.CAMERA')
this.cameraGranted = granted
}
async requestAllPermissions() {
const results = await permissionManager.requestPermissions([
'ohos.permission.CAMERA',
'ohos.permission.LOCATION',
'ohos.permission.MICROPHONE'
])
this.cameraGranted = results['ohos.permission.CAMERA']
this.locationGranted = results['ohos.permission.LOCATION']
this.microphoneGranted = results['ohos.permission.MICROPHONE']
}
build() {
Column({ space: 16 }) {
Text('权限管理')
.fontSize(24)
.fontWeight(FontWeight.Bold)
Column({ space: 12 }) {
PermissionRow('相机', this.cameraGranted, () => this.requestCamera())
PermissionRow('定位', this.locationGranted, async () => {
const granted = await permissionManager.requestPermission('ohos.permission.LOCATION')
this.locationGranted = granted
})
PermissionRow('麦克风', this.microphoneGranted, async () => {
const granted = await permissionManager.requestPermission('ohos.permission.MICROPHONE')
this.microphoneGranted = granted
})
}
.width('100%')
.padding(16)
Button('一键申请所有权限')
.onClick(() => this.requestAllPermissions())
}
.width('100%')
.height('100%')
.padding(16)
}
}
@Component
struct PermissionRow {
@Prop name: string
@Prop granted: boolean
onRequest: () => void = () => {}
build() {
Row() {
Text(this.name)
.fontSize(16)
.layoutWeight(1)
Text(this.granted ? '已授权' : '未授权')
.fontSize(14)
.fontColor(this.granted ? '#52c41a' : '#ff4d4f')
if (!this.granted) {
Button('申请')
.fontSize(12)
.onClick(this.onRequest)
}
}
.width('100%')
.height(48)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
权限申请最佳实践
- 最小权限原则:只申请应用必需的最小权限集合
- 按需申请:在需要使用某项功能时再申请对应权限,不要一次性申请所有权限
- 说明理由:申请权限时向用户说明用途,提高授权率
- 处理拒绝:用户拒绝权限后,提供降级方案或引导用户到设置页开启
- 检查状态:使用敏感功能前始终检查权限状态,不要假设权限已授权
- 及时释放:不需要定位等持续权限时及时停止,减少用户顾虑
- 权限分组:相关权限一起申请,减少弹窗次数