Android6.0中设置权限时候的SecurityException异常的处理
2017-07-26 14:51
423 查看
说来也巧了,今天碰到了一个特别奇怪的问题。上午写了一个小demo其中有一个功能是获取通讯的,大家肯定知道这时候肯定要添加一个权限:
[java] view
plain copy
print?
<!-- 读联系人权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
添加完后就开始运行了,竟然报错了,
[java] view
plain copy
print?
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2
from ProcessRecord{7ed1706 32137:com.fighting.together.oomtest/u0a166} (pid=32137, uid=10166) requires android.permission.READ_CONTACTS
or android.permission.WRITE_CONTACTS
这种获取联系人简单的东西,以前不知道做了多少遍,而且权限的申明确实已经在manifest里面,但是还是报了这种错误,真是让我很尴尬啊~
之后就开始找问题,这种奇怪的问题百度肯定是不会有结果的,我就开始想想这个项目是哪里和以前不一样了。没错,昨天刚更新了Android Studio,更新到了2.0版本了;然后!!重点来了,在build.gradle中的一个属性他也帮我改了:
[java] view
plain copy
print?
targetSdkVersion 23
没错,之前都是22的,现在变成了API23,后来查阅发现,google在API23以及之后的版本对危险权限的申请要求变的很严格了,需要多一步操作了,下文会详解。
但是你以为仅仅是这个原因导致的crash吗??no!还有一个非常重要的原因是因为我是真机调试,并且我用的nexus6,并且在前段时间刚刚通过OTA升级到了Android6.0的系统。也就说如果你的手机或者模拟器的版本是android6.0的系统,才会导致这个错误;
总结一下,如果你同时满足这三个条件你就会出现上述的crash:
1.targetSdkVersion设置为23或者更高
2.手机的操作系统为6.0或者以上
3.请求的是google定义的dangerous permissions
OK,现在是重点了,我们来分析下为什么会出现这种情况,有兴趣的同学可以去阅读google的文档http://developer.android.com/training/permissions/requesting.html
从Android6.0(API23)开始,app的需要的权限的授权过程不是在安装的过程中了,而是在app的运行中。因为用户不需要在安装或更新时候授权,所有有效的简化了app的安装过程。这样也可以给用户更多的自由去支配自己app的功能。举个例子,用户使用一个拍照软件,他会同意给这个app相机使用的权限,但是当app请求你的位置的时候,你可以选择不给他获取位置的权限。
系统权限主要分为了两种,普通权限以及危险权限:
普通权限:不会直接获取用户的隐私,如果manifest中申明了这些权限,系统会自动授予app这些普通权限。
危险权限:顾名思义,会获取用户隐私,如果你确实需要这些危险权限,用户必须在app中,通过点击同意才能获取。(相机、位置、通讯录、短信等权限)
当用户手机的android系统为5.1及以下的系统,或者项目设置的targetSDK为22或者以下,那么所有的权限(普通和危险)都是在安装时候授权的,不会出现本博客所提出的问题。
但是如果你的android系统为6.0及以上并且targetSDK为23及以上,每一个危险的权限都必须在app运行时候逐一让用户点击同意或者拒绝。
原理简单的介绍完了,下面开始具体操作:
第一.你需要导入最新的v4包
[java] view
plain copy
print?
compile 'com.android.support:support-v4:23.1.1'
第二.检测用户时候已经授权同意过该危险权限
[java] view
plain copy
print?
ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED)
上述等式返回true说明还没有授权或者已经拒绝,返回false说明用户已经同意了该危险权限,可以继续执行需要该权限的操作了。
第三.接着第二步返回true的情况,进行用户未授权或拒绝情况的处理
[java] view
plain copy
print?
ActivityCompat.shouldShowRequestPermissionRationale(context,Manifest.permission.READ_CONTACTS)
上述方法返回是一个boolean值,返回true表明用户之前已经选择过拒绝授权了;若是返回false,则表明我们还有戏,用户还没有选择过是否授权~毕竟主动权是掌握在用户手中的,此时我们就可以通过弹窗来询问用户时候同意授权了,进入第四步
第四.通过调用系统自带的弹窗询问用户是否授权
[java] view
plain copy
print?
ActivityCompat.requestPermissions(context,new String[]{Manifest.permission.READ_CONTACTS},TAG_PERMISSION);
这时一个异步的方法,TAG_PERMISSION是我们自己定义的一个int类型常量(requestCode),用于标记我们当前的方法。调用此方法,系统会弹出一个dialog,dialog的样式我们是无法修改的。如下图:
当我们选择deny或者allow都会直接回调到某一个方法中,由此我们进入第五步
第五.重写请求授权的回调方法
[java] view
plain copy
print?
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case TAG_PERMISSION: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "allow", Toast.LENGTH_SHORT).show();
getContact();
} else {
Toast.makeText(MainActivity.this, "deny", Toast.LENGTH_SHORT).show();
}
return;
}
}
}
这时候我们的TAG_PERMISSION就派上用处了,如果用户点击了授权,那么非常好!我们就可以去执行我们需要的操作了;如果用户拒绝的那么我们也只能执行被限制的操作了。
好了,最后贴上完整的代码:
[java] view
plain copy
print?
package com.fighting.together.oomtest;
import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private static final String[] SQL_COLUMN = new String[]{
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER,};
private static final int TAG_PERMISSION = 1023;
private TextView mContactTV;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContactTV = (TextView) findViewById(R.id.tv_contact);
// getContact();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS)) {
Toast.makeText(MainActivity.this, "deny for what???", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "show the request popupwindow", Toast.LENGTH_SHORT).show();
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},
TAG_PERMISSION);
}
} else {
Toast.makeText(MainActivity.this, "agreed", Toast.LENGTH_SHORT).show();
getContact();
}
}
private void getContact() {
ContentResolver resolver = getContentResolver();
Cursor phoneCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, SQL_COLUMN, null, null, null);
if (phoneCursor != null) {
while (phoneCursor.moveToNext()) {
//得到手机号码
String phoneNumber = phoneCursor.getString(0);
String contactName = phoneCursor.getString(1);
mContactTV.append("\nnumber:" + phoneNumber + ",name:" + contactName);
}
phoneCursor.close();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case TAG_PERMISSION: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "allow", Toast.LENGTH_SHORT).show();
getContact();
} else {
Toast.makeText(MainActivity.this, "deny", Toast.LENGTH_SHORT).show();
}
return;
}
}
}
}
[java] view
plain copy
print?
<!-- 读联系人权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
添加完后就开始运行了,竟然报错了,
[java] view
plain copy
print?
Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2
from ProcessRecord{7ed1706 32137:com.fighting.together.oomtest/u0a166} (pid=32137, uid=10166) requires android.permission.READ_CONTACTS
or android.permission.WRITE_CONTACTS
这种获取联系人简单的东西,以前不知道做了多少遍,而且权限的申明确实已经在manifest里面,但是还是报了这种错误,真是让我很尴尬啊~
之后就开始找问题,这种奇怪的问题百度肯定是不会有结果的,我就开始想想这个项目是哪里和以前不一样了。没错,昨天刚更新了Android Studio,更新到了2.0版本了;然后!!重点来了,在build.gradle中的一个属性他也帮我改了:
[java] view
plain copy
print?
targetSdkVersion 23
没错,之前都是22的,现在变成了API23,后来查阅发现,google在API23以及之后的版本对危险权限的申请要求变的很严格了,需要多一步操作了,下文会详解。
但是你以为仅仅是这个原因导致的crash吗??no!还有一个非常重要的原因是因为我是真机调试,并且我用的nexus6,并且在前段时间刚刚通过OTA升级到了Android6.0的系统。也就说如果你的手机或者模拟器的版本是android6.0的系统,才会导致这个错误;
总结一下,如果你同时满足这三个条件你就会出现上述的crash:
1.targetSdkVersion设置为23或者更高
2.手机的操作系统为6.0或者以上
3.请求的是google定义的dangerous permissions
OK,现在是重点了,我们来分析下为什么会出现这种情况,有兴趣的同学可以去阅读google的文档http://developer.android.com/training/permissions/requesting.html
从Android6.0(API23)开始,app的需要的权限的授权过程不是在安装的过程中了,而是在app的运行中。因为用户不需要在安装或更新时候授权,所有有效的简化了app的安装过程。这样也可以给用户更多的自由去支配自己app的功能。举个例子,用户使用一个拍照软件,他会同意给这个app相机使用的权限,但是当app请求你的位置的时候,你可以选择不给他获取位置的权限。
系统权限主要分为了两种,普通权限以及危险权限:
普通权限:不会直接获取用户的隐私,如果manifest中申明了这些权限,系统会自动授予app这些普通权限。
危险权限:顾名思义,会获取用户隐私,如果你确实需要这些危险权限,用户必须在app中,通过点击同意才能获取。(相机、位置、通讯录、短信等权限)
当用户手机的android系统为5.1及以下的系统,或者项目设置的targetSDK为22或者以下,那么所有的权限(普通和危险)都是在安装时候授权的,不会出现本博客所提出的问题。
但是如果你的android系统为6.0及以上并且targetSDK为23及以上,每一个危险的权限都必须在app运行时候逐一让用户点击同意或者拒绝。
原理简单的介绍完了,下面开始具体操作:
第一.你需要导入最新的v4包
[java] view
plain copy
print?
compile 'com.android.support:support-v4:23.1.1'
第二.检测用户时候已经授权同意过该危险权限
[java] view
plain copy
print?
ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED)
上述等式返回true说明还没有授权或者已经拒绝,返回false说明用户已经同意了该危险权限,可以继续执行需要该权限的操作了。
第三.接着第二步返回true的情况,进行用户未授权或拒绝情况的处理
[java] view
plain copy
print?
ActivityCompat.shouldShowRequestPermissionRationale(context,Manifest.permission.READ_CONTACTS)
上述方法返回是一个boolean值,返回true表明用户之前已经选择过拒绝授权了;若是返回false,则表明我们还有戏,用户还没有选择过是否授权~毕竟主动权是掌握在用户手中的,此时我们就可以通过弹窗来询问用户时候同意授权了,进入第四步
第四.通过调用系统自带的弹窗询问用户是否授权
[java] view
plain copy
print?
ActivityCompat.requestPermissions(context,new String[]{Manifest.permission.READ_CONTACTS},TAG_PERMISSION);
这时一个异步的方法,TAG_PERMISSION是我们自己定义的一个int类型常量(requestCode),用于标记我们当前的方法。调用此方法,系统会弹出一个dialog,dialog的样式我们是无法修改的。如下图:
当我们选择deny或者allow都会直接回调到某一个方法中,由此我们进入第五步
第五.重写请求授权的回调方法
[java] view
plain copy
print?
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case TAG_PERMISSION: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "allow", Toast.LENGTH_SHORT).show();
getContact();
} else {
Toast.makeText(MainActivity.this, "deny", Toast.LENGTH_SHORT).show();
}
return;
}
}
}
这时候我们的TAG_PERMISSION就派上用处了,如果用户点击了授权,那么非常好!我们就可以去执行我们需要的操作了;如果用户拒绝的那么我们也只能执行被限制的操作了。
好了,最后贴上完整的代码:
[java] view
plain copy
print?
package com.fighting.together.oomtest;
import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private static final String[] SQL_COLUMN = new String[]{
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER,};
private static final int TAG_PERMISSION = 1023;
private TextView mContactTV;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContactTV = (TextView) findViewById(R.id.tv_contact);
// getContact();
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS)) {
Toast.makeText(MainActivity.this, "deny for what???", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "show the request popupwindow", Toast.LENGTH_SHORT).show();
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_CONTACTS},
TAG_PERMISSION);
}
} else {
Toast.makeText(MainActivity.this, "agreed", Toast.LENGTH_SHORT).show();
getContact();
}
}
private void getContact() {
ContentResolver resolver = getContentResolver();
Cursor phoneCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, SQL_COLUMN, null, null, null);
if (phoneCursor != null) {
while (phoneCursor.moveToNext()) {
//得到手机号码
String phoneNumber = phoneCursor.getString(0);
String contactName = phoneCursor.getString(1);
mContactTV.append("\nnumber:" + phoneNumber + ",name:" + contactName);
}
phoneCursor.close();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case TAG_PERMISSION: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "allow", Toast.LENGTH_SHORT).show();
getContact();
} else {
Toast.makeText(MainActivity.this, "deny", Toast.LENGTH_SHORT).show();
}
return;
}
}
}
}
相关文章推荐
- Android6.0中设置权限时候的SecurityException异常的处理
- android6.0版本的权限申请处理
- Android6.0中对权限的处理
- 收藏精简Android6.0运行时权限处理,70行代码工具类
- Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码
- Android6.0运行时权限的处理
- C# 网络连接中异常断线的处理:ReceiveTimeout, SendTimeout 及 KeepAliveValues(设置心跳)
- 当权限正常以及连接正常时,WMI出现莫名COM异常的处理
- Android6.0 权限处理
- hive 不同用户 权限设置 出错处理
- 处理svn设置了权限仍然提交不了
- 实践证明:当类想实现两个监听接口的时候,必须把两个都设置成内部类,不可能一个是外部类实现,一个是内部类实现。这样容易捕获错误,出现异常。
- SpringBoot学习——全局异常处理设置(返回JSON)
- Android6.0及以上版本一次请求多个权限的处理方式
- php异常处理—设置顶层异常处理器
- Android6.0之前访问权限被拒绝如何处理
- @RequiresPermissions 控制权限的异常处理以及Ajax方式请求时返回json
- Oracle EBS - OM: 发运事务处理组织访问权限分配设置
- Web应用中设置了异常统一处理页面,捕捉指定异常时的处理问题
- 如何在deploy的时候 为数据库,表,存储过程 设置权限