we new

Android运行时权限

闲的没事做一个个人的app,把自己经常用到的全部写上,譬如刷自己的网站,发送邮件,打1008611查话费之类的(p.s. 搞的经常用一样~)。在写Intent来调用手机拨号功能的时候,一开始调用 ACTION_DIAL 没问题,结果我看到了 ACTION_CALL 就在想少按一个键好像能轻松不少的样子(p.s. 搞的跟真的一样~)。结果一直调没出来这就很尴尬,然后就有下面的一系列折腾。

过程详述

提示是Call_Phone的权限没加,然后就到Manifest加上

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

还是没有什么卵用。
然后查到stackoverflow有人回答说是api是23+的话,需要判断程序 permissions runtime。修改之后,AS提示对23一下的也要兼容,参考了一下这个解决方法,加了两个判断,运行没问题了。代码是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M) {
if (MainActivity.this.checkSelfPermission(Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
callPhone();
}
else{
Log.i("test", "User doesnot agree this permission");
requestPermissions(new String[]{Manifest.permission.CALL_PHONE},REQUEST_CODE_ASK_PERMISSIONS);
}
}
else{
callPhone();
}
void callPhone(){
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:1008611"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}

知识学习

参考文章:一行代码解决Android M新的运行时权限问题Android M 新的运行时权限开发者需要知道的一切

什么是运行时权限

Android API在23以后对权限管理系统进行了改版,之前我们的App需要权限,只需在manifest中申明即可,用户安装后,一切申明的权限都可来去自如的使用。但是Android M把权限管理做了加强处理,在manifest申明了,在使用到相关功能时,还需重新授权方可使用。这些需要重新授权方可使用的权限称之为运行时权限。

哪些属于运行权限

Google给出的运行权限表中:
运行权限表
在表中同一个权限组的任何一个权限被授权了,同组的其他权限也自动被授权。

代码

代码封装

在自己的BaseActivity中添加一下权限控制代码

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
//**************** Android M Permission (Android 6.0权限控制代码封装)
*****************************************************
private int permissionRequestCode = 88;
private PermissionCallback permissionRunnable ;
public interface PermissionCallback{
void hasPermission();
void noPermission();
}
/**
* Android M运行时权限请求封装
* @param permissionDes 权限描述
* @param runnable 请求权限回调
* @param permissions 请求的权限(数组类型),直接从Manifest中读取相应的值,比如Manifest.permission.WRITE_CONTACTS
*/
public void performCodeWithPermission(@NonNull String permissionDes,PermissionCallback runnable,@NonNull String... permissions){
if(permissions == null || permissions.length == 0)return;
// this.permissionrequestCode = requestCode;
this.permissionRunnable = runnable;
if((Build.VERSION.SDK_INT < Build.VERSION_CODES.M) || checkPermissionGranted(permissions)){
if(permissionRunnable!=null){
permissionRunnable.hasPermission();
permissionRunnable = null;
}
}else{
//permission has not been granted.
requestPermission(permissionDes,permissionRequestCode,permissions);
}
}
private boolean checkPermissionGranted(String[] permissions){
boolean flag = true;
for(String p:permissions){
if(ActivityCompat.checkSelfPermission(this, p) != PackageManager.PERMISSION_GRANTED){
flag = false;
break;
}
}
return flag;
}
private void requestPermission(String permissionDes,final int requestCode,final String[] permissions){
if(shouldShowRequestPermissionRationale(permissions)){
// Provide an additional rationale to the user if the permission was not granted
// and the user would benefit from additional context for the use of the permission.
// For example, if the request has been denied previously.
// Snackbar.make(getWindow().getDecorView(), requestName,
// Snackbar.LENGTH_INDEFINITE)
// .setAction(R.string.common_ok, new View.OnClickListener() {
// @Override
// public void onClick(View view) {
// ActivityCompat.requestPermissions(BaseAppCompatActivity.this,
// permissions,
// requestCode);
// }
// })
// .show();
//如果用户之前拒绝过此权限,再提示一次准备授权相关权限
new AlertDialog.Builder(this)
.setTitle("提示")
.setMessage(permissionDes)
.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(BaseAppCompatActivity.this, permissions, requestCode);
}
}).show();
}else{
// Contact permissions have not been granted yet. Request them directly.
ActivityCompat.requestPermissions(BaseAppCompatActivity.this, permissions, requestCode);
}
}
private boolean shouldShowRequestPermissionRationale(String[] permissions){
boolean flag = false;
for(String p:permissions){
if (ActivityCompat.shouldShowRequestPermissionRationale(this,p)){
flag = true;
break;
}
}
return flag;
}
/**
* Callback received when a permissions request has been completed.
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if(requestCode == permissionRequestCode){
if(verifyPermissions(grantResults)){
if(permissionRunnable!=null) {
permissionRunnable.hasPermission();
permissionRunnable = null;
}
}else{
showToast("暂无权限执行相关操作!");
if(permissionRunnable!=null) {
permissionRunnable.noPermission();
permissionRunnable = null;
}
}
}else{
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
public boolean verifyPermissions(int[] grantResults) {
// At least one result must be checked.
if(grantResults.length < 1){
return false;
}
// Verify that each required permission has been granted, otherwise return false.
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
//********************** END Android M Permission ****************************************

注意:

  • BaseActivity要继承AppCompatActivity
  • support包使用尽量新的,我使用的是compile ‘com.android.support:appcompat-v7:23.0.1’ 以防里面的ActivityCompat找不到相关类或方法。
  • 如果在Fragment中使用,直接在自己的BaseFragment写个方法调用此Activity的方法即可。
1
2
3
4
5
6
7
8
9
10
11
/**
* Android M运行时权限请求封装
* @param permissionDes 权限描述
* @param runnable 请求权限回调
* @param permissions 请求的权限(数组类型),直接从Manifest中读取相应的值,比如Manifest.permission.WRITE_CONTACTS
*/
public void performCodeWithPermission(@NonNull String permissionDes,BaseAppCompatActivity.PermissionCallback runnable,@NonNull String... permissions){
if(getActivity()!=null && getActivity() instanceof BaseAppCompatActivity){
((BaseAppCompatActivity) getActivity()).performCodeWithPermission(permissionDes,runnable,permissions);
}
}

代码使用

比如,要请求相机:

1
2
3
4
5
6
7
8
9
performCodeWithPermission("XX App请求访问相机权限",new BaseAppCompatActivity.PermissionCallback() {
@Override
public void hasPermission() {
//执行打开相机相关代码
}
@Override
public void noPermission() {
}
}, Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE);

深入研究

checkSelfPermission检测权限失效问题:

  1. If your application is targeting an API level before 23 (Android M) then both: ContextCompat#checkSelfPermission and Context#checkSelfPermission doesn’t work and always returns 0 (PERMISSION_GRANTED). Even if you run the application on Android 6.0 (API 23).
  2. It’s not fully true that if you targeting an API level before 23 then you don’t have to take care of permissions. If you targeting an API level before 23 then: Android < 6.0: Everything will be ok; Android 6.0: Application’s run-time permissions will be granted by default (compatibility mode applies), but the user can change run-time permissions in Android Settings, then you may have a problem.
  3. As I said in the 1st point, if you targeting an API level before 23 on Android 6.0 then ContextCompat#checkSelfPermission and Context#checkSelfPermission doesn’t work. Fortunately you can use PermissionChecker#checkSelfPermission to check run-time permissions.

声明: 本文转载前需与作者联系并标明出处
分享到: