从 Android 6.0 (API 23) 开始系统开始将应用权限收紧,将权限分为普通(Normal Permissions)权限与危险权限(Dangerous Permissions)。

普通权限不会影响到用户的隐私,只要像以往那样在 manifest 中声明即可使用。
危险权限不仅要在 manifest 中声明,而且需要获得用户的批准。虽然可以临时将 target API 设置在 23 以下规避,但一来用户仍然可以在系统设置中强制拒绝授予权限造成应用崩溃,二来也并非长久之计。

危险权限及分组

系统分组处理危险权限的授予,如果应用已经获得某个组内的某项权限,那么也就默认获得了该组内的其他权限。比如我们已经获得了 READ_CONTACTS 权限,那么相当于 CONTACTS 组内的 WRITE_CONTACTSGET_ACCOUNTS 权限也获得了。

Permission Group Permissions
CALENDAR
READ_CALENDAR
WRITE_CALENDAR
CAMERA CAMERA
CONTACTS
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE
READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

基本 API 使用

与运行时权限相关的 API 主要有以下几个方法。

1
2
3
4
5
6
// Context/ContextCompat
int checkSelfPermission (String permission)
// Activity/ActivityCompat
boolean shouldShowRequestPermissionRationale (String permission)
void requestPermissions (String[] permissions, int requestCode)
void onRequestPermissionsResult (int requestCode, String[] permissions, int[] grantResults)

checkSelfPermission 方法用来检测是否已经获得某项权限,返回结果为PackageManager.PERMISSION_GRANTEDPackageManager.PERMISSION_DENIED

shouldShowRequestPermissionRationale 表示应用是否应该向用户解释为何需要权限。未请求过该权限时返回 false。

requestPermissions 执行请求权限的实际操作,在调用该方法时,系统弹出对话框让用户选择是否允许获取所请求的权限。
如果用户选择了 Never ask again 并拒绝授予,那么之后调用 requestPermissions 请求该权限直接会被拒绝,shouldShowRequestPermissionRationale 都会返回 false。

onRequestPermissionsResult 方法是 Activity 的一个回调方法,类似于 onActivityResult,返回权限请求的结果。

例子

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
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// 未获得该权限
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// 被拒绝过,向用户解释为何需要该权限后再请求
showPermissionExplanation();
} else {
// 之前从未请求过该权限或被永久拒绝,直接发起请求
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
PERMISSION_REQUEST_STORAGE);
}
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_STORAGE) {
for (int i = 0; i < permissions.length; i++) {
if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permissions[i])) {
if (PackageManager.PERMISSION_GRANTED == grantResults[i]) {
// 成功获得权限
} else {
// 被拒绝
// 如果该权限必须的话,可以检查 shouldShowRequestPermissionRationale
// 若返回 true,表示用户永久拒绝,可以引导用户在系统设置中重新授予权限
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
showPermissionGuide();
}
}
}
}
}
}

参考资料