吴小龙同學

Android 6.0 RuntimePermission

android6.0棉花糖,app将不会在安装的时候授予权限。取而代之的是,app不得不在运行时一个一个询问用户授予权限。

1
2
3
4
5
6
7
8
9
10
android {
compileSdkVersion 23
...
defaultConfig {
...
targetSdkVersion 23
...
}
}

如何app/build.gradle是23的话,在6.0上运行一些权限,没有做处理,会直接崩溃:

1
2
3
4
5
6
...
Caused by: java.lang.SecurityException: Permission Denial:
opening provider com.android.providers.contacts.ContactsProvider2 from ProcessRecord
{c3b3c66 16712:com.wuxiaolong.apksample/u0a62} (pid=16712, uid=10062)
requires android.permission.READ_CONTACTS or android.permission.WRITE_CONTACTS
...

如果targetSdkVersion 22,就不会发生这样的错误,下面就来实践下这个6.0运行的权限。

效果预览

按照惯例,先上效果图:

说明:第一次请求授权,是没有“不再询问”,拒绝后,再次请求授权,就会出现“不再询问”。

实践

AndroidManifest.xml

1
<uses-permission android:name="android.permission.READ_CONTACTS" />

代码调用

在需要使用到权限之前调用以下代码:

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
private void mayRequestContacts() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
switch (checkSelfPermission(Manifest.permission.READ_CONTACTS)) {
case PackageManager.PERMISSION_GRANTED:
// 已有授权
Log.i("wxl", "已有授权");
break;
case PackageManager.PERMISSION_DENIED:
// 没有权限:尚未请求过权限,
// 或者请求授权被拒绝,
// 或者曾经授权过,但被用户在设置中禁用权限
Log.i("wxl", "没有权限:尚未请求过权限,或者请求授权被拒绝," +
"或者曾经授权过, 但被用户在设置中禁用权限");
/**
* 如果用户勾上了“不再询问”,这时候就不应弹请求权限的对话框,
* 可以在请求权限之前用
* shouldShowRequestPermissionRationale判断用户是否拒绝过,
* 如果返回true,表示用户拒绝过。
*/
Log.i("wxl", "shouldShowRequestPermissionRationale=="
+ shouldShowRequestPermissionRationale(READ_CONTACTS));
if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {
Snackbar.make(mEmailView,
R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
.setAction(android.R.string.ok, new View.OnClickListener() {
@Override
@TargetApi(Build.VERSION_CODES.M)
public void onClick(View v) {
requestPermissions(new String[]{READ_CONTACTS},
REQUEST_READ_CONTACTS);
}
}).show();
} else {
requestPermissions(new String[]{READ_CONTACTS},
REQUEST_READ_CONTACTS);
}
break;
default:
break;
}
}
}

checkSelfPermission:检查权限
requestPermissions:请求权限
shouldShowRequestPermissionRationale:判断用户是否拒绝过

授权回调

不管是允许或拒绝,都会回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,@NonNull int[] grantResults) {
if (requestCode == REQUEST_READ_CONTACTS) {
if (grantResults.length == 1 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 授权请求被通过,读取通讯录
Log.i("wxl", "授权请求被通过,读取通讯录");
} else {
Log.i("wxl", "授权请求不被通过");
}
}
}

多个权限

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
@TargetApi(Build.VERSION_CODES.M)
private void checkPermissions() {
List<String> permissionsNeeded = new ArrayList<>();
final List<String> permissionsList = new ArrayList<>();
if (deniedPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
permissionsNeeded.add("GPS");
if (deniedPermission(permissionsList, Manifest.permission.READ_CONTACTS))
permissionsNeeded.add("Read Contacts");
if (permissionsList.size() > 0) {
if (permissionsNeeded.size() > 0) {
// Need Rationale
String message = "You need to grant access to " + permissionsNeeded.get(0);
for (int i = 1; i < permissionsNeeded.size(); i++)
message = message + ", " + permissionsNeeded.get(i);
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), REQUEST_PERMISSIONS);
}
}
}
@TargetApi(Build.VERSION_CODES.M)
private boolean deniedPermission(List<String> permissionsList, String permission) {
if (checkSelfPermission(permission) == PackageManager.PERMISSION_DENIED) {
permissionsList.add(permission);
// true,表示用户拒绝过
if (shouldShowRequestPermissionRationale(permission)) {
return true;
}
}
return false;
}
/**
* Callback received when a permissions request has been completed.
*/
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_PERMISSIONS) {
Map<String, Integer> perms = new HashMap<String, Integer>();
// Initial
perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
perms.put(Manifest.permission.READ_CONTACTS, PackageManager.PERMISSION_GRANTED);
// Fill with results
for (int i = 0; i < permissions.length; i++)
perms.put(permissions[i], grantResults[i]);
// Check for ACCESS_FINE_LOCATION
if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
&& perms.get(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
// All Permissions Granted
Log.i("wxl", "授权请求被通过");
} else {
// Permission Denied
Log.i("wxl", "授权请求不被通过");
}
}
}

权限分组

权限那么多,如果一个个判断,岂不是会疯掉,如图:

同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_CONTACTS被授权了,app也有READ_CONTACTS和GET_ACCOUNTS了。

关于作者

点击查看

附录

Android M 新的运行时权限开发者需要知道的一切
Android Runtime Permission测试



联系我

我的微信公众号:吴小龙同学,欢迎关注交流~

赞助商:躲角落的猫咪