Android开发——Android 6.0权限管理机制详解

0.前言

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

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

我也花了整整两天的时间对兼容Android6.0权限管理机制的整个处理过程进行了理解和汇总,也方便大家遇到类似的问题少走弯路。

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{
                    //用户一直拒绝并一直不勾选“不再提醒”
                    //Toast提醒一下即可
                }

在需要权限的时候,只要执行类似于下面的检查和申请权限的代码即可(注意自己替换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...
}
时间: 2024-10-01 02:21:25

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

Android内存管理机制详解 (zhuan)

http://www.2cto.com/kf/201212/175786.html 与windows内存区别 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是Linux内存管理的一个优秀特性,在这方面,区别于 Windows的内存管理.主要特点是,无论物理内存有多大,Linux都将其充份利用,将一些程序调用过的硬盘数据读入内存,利用内存读写的高速特性来提高Linux系统的数据访问性能.而Windows是只在需要内存时,才为应用程序分配内存,

Android研究之英特尔 Android* 开发人员指南上的对等应用详解

简介 当没有 Wi-Fi 访问点或互联网访问时,Android* 应用可能需要对等连接在两台或多台 Android* 设备之间建立连接. 比如,文件共享应用和多人游戏. 该功能可使用 NFC.蓝牙或 Wi-Fi 对等技术来实施. 特定案例中的首选技术需要根据所需的距离.连接速度.功耗和独特的技术特性来决定. 本文将对 Wi-Fi 对等技术进行评估. Wi-Fi 对等(P2P)支持具备适当应用的 Android 4.0 或更高版本在没有接入点的情况下通过 Wi-Fi 彼此连接. Android W

Android 内存管理机制详解

??嵌入式设备的一个普遍特点是内存容量相对有限.当运行的程序超过一定数量时,或者涉及复杂的计算时,很可能出现内存不足,进而导致系统卡顿的现象.Android 系统也不例外,它同样面临着设备物理内存短缺的困境.对于已经启动过一次的Android程序,再一次启动所花的时间会明显减少.原因在于Android系统并不马上清理那些已经"淡出视野"的程序(比如你调用Activity.finish退出UI界面).它们在一定的时间里仍然驻留在内存中.这样做的好处是明显的,即下一次启动不需要再为程序重新

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

回调函数http://blog.csdn.net/a78270528/article/details/46918601 Android框架 android frame work: http://blog.csdn.net/bsmmaoshenbo/article/details/52506530 15 个 Android 通用流行框架大全 http://blog.csdn.net/bsmmaoshenbo/article/details/52627219

memcached内存管理机制详解

    我们知道,memcached是一个内存缓存系统,因此对于内存的管理是需要使用者了解的.本文将对memcached的内存模型及管理机制做一个详细的描述. 基本概念 在开始之前,有必要先了解几个基本概念: 1.slab class:在memcached中,对元素的管理是以slab为单元进行管理的.每个slab class对应一个或多个空间大小相同的chunk.参考下图一. 2.chunk:存放元素的最小单元.用户数据item(key.value等)最终会保存在chunk中.memcach

淘宝(阿里百川)手机客户端开发日记第六篇 广播机制详解(一)

public abstract class BroadcastReceiver; Base class for code that will receive intents sent by sendBroadcast(). If you don't need to send broadcasts across applications, consider using this class with LocalBroadcastManager instead of the more general

ARC内存管理机制详解

ARC在OC里面个人感觉又是一个高大上的牛词,在前面Objective-C中的内存管理部分提到了ARC内存管理机制,ARC是Automatic Reference Counting---自动引用计数.有自动引用计数,那么就得有手动引用计数MRC(Mannul Reference Counting),前面已经提到过了MRC.那么在ARC模式下是不是意味着我们就可以一点也不用进行内存管理的呢?并不是这样的,我们还需要代码进行内存的管理.下面会结合着代码把OC中的ARC机制做一个详细的总结(欢迎大家批

Android源码分析--Handler和Looper机制详解

在Android系统中的应用程序,与Java的应用程序相同,都是靠消息驱动,简单的说就是:有一个消息队列,我们可以不断的向这个消息队列中添加消息,并从中取出消息,处理消息.Android中与此工作相关的主要是由Handler,Looper以及Message来完成. Looper类:为一个线程运行着一个消息循环,内部有一个消息队列,每一个线程只允许最多存在一个Looper: Handler类:允许你向一个线程的消息队列中发送消息,处理消息: Message类:消息类. 使用样例 首先,我们通过一个

Android开发 ExpandableListView 列表内容下拉View详解

前言 在需要实现一个List的item需要包含列表的时候,我们就可以选择ExpandableListView. 其实这个View的原始设计还是ListView的那套.就是增加2层的ListView而已.所以在写它的适配器与ListView的适配器挺相似的,所以会有一个通病就是没有Item的View的复用机制请一定要注意这点,在实现使用的时候需要写Item的View的复用,减少内存与增加性能. 一个简单的Demo 老规矩,先来一个最简单的demo来了解下最基本的使用方法.注意!这个demo是没有在