0.前言

最近在研究所实习,我负责维护Android手机取证项目的Android客户端,有客户反映我们的APP在Android6.0无响应,经过调试发现SD卡读写权限权限被拒绝。但明明是在AndroidManifest.xml文件中声明过的。查了很多资料才知道Android6.0的很多权限申请机制发生了改变,可以说是Android6.0在安全机制上更进了一步吧,因此写下这篇文章以记录。

注:在运行程序时,对于某些权限向用户询问申请(后面会详细地讲)时因为我们知道客户在我们APP中不会点“拒绝”,因此我对此功能的实现也仅限于文章中的前部分直接做死循环直到用户同意授权该权限,因为当然是用最低的成本满足客户的需求最好啦,但是真正开发中,需要处理很多事情,我也并没有浅尝辄止,后面会用到onRequestPermissionsResult回调方法,shouldShowRequestPermissionRationale方法等,才能避免很多令用户困扰的情况。后面会详细地进行介绍。

我也花了整整两天的时间对兼容Android6.0权限管理机制的整个处理过程进行了理解和汇总,也方便大家遇到类似的问题少走弯路。本文原创,转载请标明出处:http://blog.csdn.net/seu_calvin/article/details/52163456

1.Android 6.0新的权限管理机制

Android 6.0 Marshmallow版本之后,系统对于一些危险级别的权限,在运行那些targetSdkVersion设置为23和23以上的应用并且需要这些权限时,会一个一个询问用户是否授予权限。若不询问直接使用这些权限,会出现类似java.lang.SecurityException: Permission Denial的异常日志。

应用targetSdkVersion如果没有设置为23版本或者以上,系统还是会使用旧规则:在安装的时候赋予该app所申请的所有权限。因此不会影响以前应用的正常使用,但是6.0以后,用户可以在<设置-权限>里将该APP的某些权限手动关闭,此时被用户禁止权限的API接口返回值都为null或者0,我们判空即可防止App Crash。

若APP在运行时,将设置里的权限手动关闭,那就会直接Crash。

2.危险权限列表

前面提到的危险级别的权限是我们需要格外关注的,因为这些权限在使用前需要进行特殊处理。

上图中有一个权限群的概念,同一组的任何一个权限被授权了,其他权限也自动被授权。

例如,一旦WRITE_EXTERNAL_STORAGE被授权了,APP同时也就有了READ_EXTERNAL_STORAGE权限。

3.Android 6.0运行时主动请求权限


3.1  检测和申请权限

下面的例子介绍上面列出的读写SD卡的使用例子,可以使用以下的方式解决:

public boolean isGrantExternalRW(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && activity.checkSelfPermission(
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { activity.requestPermissions(new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
}, 1);
return false;//第一次开启应用并执行权限检查,虽然返回了false,但是已经调用过了申请权限的方法
}
return true;//非第一次开启应用并执行权限检查,或者6.0以下的Android版本
}

这里需要说明的是,检查和申请权限的方法分别是Activity.checkSelfPermission()和Activity.requestPermissions,这两个方法是在 API 23中新增的。

Activity.checkSelfPermission()主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。

Activity.requestPermissions该方法是异步的,第一个参数是需要申请的权限的字符串数组,第二个参数为requestCode,主要用于回调的时候检测。最前面的参数可以传入mActivity。好像不传也可以。

可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。

然后在需要使用这个权限的时机,进行如下调用即可。

 boolean isGrant= isGrantExternalRW(mActivity);
if (isGrant) {
//业务逻辑
}
while (!isGrant) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
isGrant = isGrantExternalRW(mAct);
if (isGrant) {
//业务逻辑
}
}

这里因为我们写的isGrantExternalRW方法的返回值只是判断的一开始检查权限时的状态,因此如果是第一次开启应用,返回的是false,并且申请了权限(如果用户同意的话),再调用一次该方法返回true,进入逻辑代码,并最后跳出while循环。如果用户已经授权过了,那么直接会走逻辑代码。这里比较流氓的是,如果用户一直不同意,会一直返回false,我们就阻塞在while循环里,一秒后继续申请,直到用户同意为止。显然这是不够友好的。

因此Google为了防止这种情况的发生,在用户拒绝授权时,下一次弹窗可以勾选“不再提醒”。如果这个选项被用户勾选了。下次为这个权限请求requestPermissions时,对话框就不弹出来了,系统会直接回调处理申请返回结果的回调方法onRequestPermissionsResult,回调结果为最后一次用户的选择。

3.2 处理权限申请回调

@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case 1: {
// 用户取消授权这个数组为空,如果你同时申请两个权限,那么grantResults的length就为2,分别记录你两个权限的申请结果
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//业务逻辑
} else {
//授权被拒绝,不再进行基于该权限的功能
}
return;
}
// 其他case处理其他权限的申请回调
}
}

上面处理申请回调的方法已经写的很明白了。还有就是不管用户点击拒绝还是同意,都会回调该方法(别忘记用户拒绝并勾选“不再提醒”时也会回调)。

3.3 使用shouldShowRequestPermissionRationale方法


问题来了,如果第二次向用户申请权限被拒绝,并且用户勾选了“不再提醒”,那我们以后每次需要使用这个权限都会直接被拒绝,并不会弹出对话框。APP什么也不做会产生很差的用户体验。所以这种情况需要我们进行处理。这时候我们可以借助shouldShowRequestPermissionRationale方法。首先看一下该方法的返回值。

因此,我们在回调函数中做权限检测,如果返回DENIED,就调用上述方法,返回false,就弹出对话框引导用户手动开启权限,避免了用户的操作触发了权限申请机制(已被拒绝并勾选不再提醒或手动关闭权限),但是没有任何响应的尴尬。又因为我们事先向用户询问授权过了,因此不存在表格中的第一种情况。

因此只需要在上面回调代码的else代码段加入如下代码即可。

                if (!mActivity.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
//用户已经完全拒绝,或手动关闭了权限
//开启此对话框缓解一下尴尬...
AlertDialog dialog = new AlertDialog.Builder(this)
.setMessage("不开启该权限将无法正常工作,请在设置中手动开启!")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
}).create();
dialog.show();
return;
}else{
//用户一直拒绝并一直不勾选“不再提醒”
//不执行该权限对应功能模块,也不用提示,因为下次需要权限还会弹出对话框
}

在需要权限的时候,只要执行类似于下面的检查和申请权限的代码即可(注意自己替换ContentCompat):

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE); }
else { //Do the stuff that requires permission...
}

转载请标明出处:http://blog.csdn.net/seu_calvin/article/details/52163456

Android开发——Android 6.0权限管理机制详解的更多相关文章

  1. 整理最全的Android开发工程师面试题,面试题详解。java、Android程序员

    1.    请描述下Activity的生命周期. 必调用的三个方法:onCreate()--> onStart() --> onResume(),用AAA表示 (1)父Activity启动 ...

  2. Android 回调接口是啥,回调机制详解(zhuan)

    回调函数http://blog.csdn.net/a78270528/article/details/46918601 Android框架 android frame work: http://blo ...

  3. object-c(oc)内存管理机制详解

    1.内存的创建和释放 让我们以Object-c世界中最最简单的申请内存方式展开,谈谈关于一个对象的生命周期.首先创建一个对象: 1 2 3 //“ClassName”是任何你想写的类名,比如NSStr ...

  4. mybatis事务管理机制详解

    1.mybatis事务的配置和使用 mybatis事务有两种使用方式: (a):使用JDBC的事务管理机制:即使用java.Sql.Connection对象完成对事务的提交,回滚和关闭操作. (b): ...

  5. ARC内存管理机制详解

    ARC在OC里面个人感觉又是一个高大上的牛词,在前面Objective-C中的内存管理部分提到了ARC内存管理机制,ARC是Automatic Reference Counting---自动引用计数. ...

  6. linux初级学习笔记八:linux权限管理及权限管理命令详解!(视频序号:04_2)

    本节学习的命令:chown,chgrp,chmod,openssl,umask 本节学习的技能: 文件权限详解及对其的操作 生成随机密码命令 用遮罩码对用户权限进行修改 站在用户登陆的角度来说SHEL ...

  7. Android开发之数据存储——SharedPreferences基础知识详解,饿补学会基本知识,开发者必会它的用法。

    一.数据存储选项:Data Storage --Storage Options[重点] 1.Shared Preferences Store private primitive data in key ...

  8. 【转】Android 最火框架XUtils之注解机制详解

    原文:http://blog.csdn.net/rain_butterfly/article/details/37931031 在上一篇文章Android 最火的快速开发框架XUtils中简单介绍了x ...

  9. Android开发 设备横屏与竖屏的详解

    需要了解横竖屏切换关键知识 1.在Android设备的横竖屏幕,每一次切换横竖屏其实是在重新创建Activity,Activity会重新走一遍生命周期.从onCreate 到 onDestroy 2. ...

随机推荐

  1. springboot springmvc 抛出全局异常解决方法

    springboot中抛出异常,springboot自带的是springmvc框架,这个就不多说了. springmvc统一异常解决方法这里要说明的是.只是结合了springboot的使用而已.直接上 ...

  2. yum指令之修复

    折腾着搞 openvpn 网站服务器 yum指令 出了点问题 ------------------------------------------------------------ [root@cl ...

  3. Eclipse - 安装语言包

    Open the install wizard with 'Help' > 'Install new software...' add the Babel p2 repository: http ...

  4. 有关在python中使用Redis(一)

    python作为一种处理数据的脚本语言本身有许多方法函数供大家使用,有时候为了提升数据处理速度(如海量数据的访问或者海量数据的读取),涉及分布式管理架构,可能需要用到Redis,Redis是一个开源的 ...

  5. 【复习笔记】CSS基础

    外观 color:rgba(255,255,255,1),a表示alpha,透明度值0~1 font-family:字体1,字体2,字体3;确保某字体不存在时自动选择下一个,最好使用字体的英文名称保证 ...

  6. 零基础逆向工程17_PE结构01_PE头解析_手动

    PE文件的两种状态 1.在硬盘中 节省硬盘空间 硬盘对齐 内存对齐 2.在内存中 3.PE磁盘文件与内存映像结构图 PE文件为什么要分节 -- 手动解析:PE文件 分析软件:飞鸽传书http://ww ...

  7. vim的命令

    下面是从一个博客里摘抄出来的, 供自己学习使用.   在命令状态下对当前行用== (连按=两次), 或对多行用n==(n是自然数)表示自动缩进从当前行起的下面n行.你可以试试把代码缩进任意打乱再用n= ...

  8. Volley解析(一)--Volley的使用

    Volley解析(一)--Volley的使用 Volley 是一个HTTP协议的网络请求框架 Volley的优势: 自动安排网络请求 支持多个并发网络连接 具有标准HTTP缓存一致性的透明磁盘和内存响 ...

  9. ArcServer10.1系列产品之ArcGIS Web Adaptor (IIS)

    1.关于 ArcGIS Web Adaptor 通过 ArcGIS Web Adaptor,可以将 ArcGIS for Server 与您现有的 Web 服务器进行集成.ArcGIS Web Ada ...

  10. Linux shell标准输入,标准输出,错误输出

    shell中可能经常能看到:1>/dev/null  2>&1  eg:sudo kill -9 `ps -elf |grep -v grep|grep $1|awk '{prin ...