android6.0的权限讲解

来源:互联网 发布:mac 下面菜单隐藏 编辑:程序博客网 时间:2024/05/18 02:03

本文转自 点击打开链接

在android permission权限与安全机制解析(上)篇博客中,我已经详细介绍了android相关系统permission和自定义permission,以及一些权限机制和安全机制。这篇博客主要将会介绍到android 6.0的相关权限更改,原理和相关的处理方式,解决方法等。 
  就以我以前的一个仿最新版微信相册为例子来分析。

android 6.0权限全面详细分析和解决方案

Marshmallow版本权限修改

  android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次。一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西,而且一般用户安装的时候很少会去仔细看权限列表,更不会去深入了解这些权限可能带来的相关危害。所以在android 6.0 Marshmallow版本之后,系统不会在软件安装的时候就赋予该app所有其申请的权限,对于一些危险级别的权限,app需要在运行时一个一个询问用户授予权限。 
  这里写图片描述

旧版本app兼容问题

  那么问题来了,是不是所有以前发布的app都会出现问题呢?答案是不会,只有那些targetSdkVersion 设置为23和23以上的应用才会出现异常,在使用危险权限的时候系统必须要获得用户的同意才能使用,要不然应用就会崩溃,出现类似

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-label" style="box-sizing: border-box;">java.lang.SecurityException:</span> Permission Denial: reading <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">com</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.android</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.providers</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.media</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.MediaProvider</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

的崩溃日志。所以targetSdkVersion如果没有设置为23版本或者以上,系统还是会使用旧规则:在安装的时候赋予该app所申请的所有权限。所以app当然可以和以前一样正常使用了,但是还有一点需要注意的是6.0的系统里面,用户可以手动将该app的权限关闭,如下图 
  这里写图片描述 
  那么问题又来了,如果以前的老应用申请的权限被用户手动关闭了怎么办,应用会崩溃么?我们来试一试 
  这里写图片描述 
  好吧,可以庆幸了一下了,不会抛出异常,不会崩溃,只不过调用那些被用户禁止权限的api接口返回值都为null或者0,所以我们只需要做一下判空操作就可以了,不判空当然还是会崩溃的喽。

普通权限和危险权限列表

  现在对于新版本的权限变更应该有了基本的认识,那么,是不是所有权限都需要去进行特殊处理呢?当然不是,只有那些危险级别的权限才需要,如列表所示: 
  这里写图片描述 
  android开发者官网也有相关描述: 
  http://developer.android.com/training/permissions/requesting.html 
  http://developer.android.com/guide/topics/security/permissions.html#normal-dangerous 
  所以仔细去看看自己的app,对照列表,如果有需要申请其中的一个权限,就需要进行特殊操作。还有一个比较人性的地方就是如果同一组的任何一个权限被授权了,其他权限也自动被授权。例如,一旦WRITE_EXTERNAL_STORAGE被授权了,app也有READ_EXTERNAL_STORAGE权限了。

支持Marshmallow新版本权限机制

  终于要开始支持android 6.0版本了,最先一步当然就是修改build.gradle文件中的tragetSdkVersion和compileSdkVersion成23版本,同时使用compile ‘com.android.support:appcompat-v7:23.1.1’最新v7包。

<code class="hljs r has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">android {    compileSdkVersion <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">23</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>    defaultConfig {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>        targetSdkVersion <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">23</span>        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>    }}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>dependencies {<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>compile <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'com.android.support:appcompat-v7:23.1.1'</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li></ul>

  修改完后,感兴趣的朋友可以直接打包在手机上测试一下,看看是不是会出现类似于上面我说的那些崩溃日志。 
  接着下一步当然就是要修改代码了,最原始代码,无任何处理:

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">private void startGetImageThread(){....    Uri uri = MediaStore<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Images</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Media</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.EXTERNAL</span>_CONTENT_URI<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>    ContentResolver contentResolver = getContentResolver()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>    //获取jpeg和png格式的文件,并且按照时间进行倒序    Cursor cursor = contentResolver<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.query</span>(uri, null, MediaStore<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Images</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Media</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.MIME</span>_TYPE + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"=\"image/jpeg\" or "</span> +    MediaStore<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Images</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Media</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.MIME</span>_TYPE + <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"=\"image/png\""</span>, null, MediaStore<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Images</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.Media</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.DATE</span>_MODIFIED+<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">" desc"</span>)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>    ....}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

  这段代码需要访问外部存储(相册图片),属于危险级别的权限,直接使用会造成应用崩溃,所以在这段代码执行之前我们需要进行特殊处理:

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">int hasWriteContactsPermission = checkSelfPermission(Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.WRITE</span>_EXTERNAL_STORAGE)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>if (hasWriteContactsPermission != PackageManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.PERMISSION</span>_GRANTED) {        requestPermissions(new String[] {Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.WRITE</span>_EXTERNAL_STORAGE},                CODE_FOR_WRITE_PERMISSION)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>    return<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li></ul>

  写完这段代码之后,就会出现如下系统dialog: 
  这里写图片描述 
  紧接着就需要去处理DENY和ALLOW的回调了,重写onRequestPermissionsResult函数:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onRequestPermissionsResult</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> requestCode, String[] permissions, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>[] grantResults) {    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (requestCode == CODE_FOR_WRITE_PERMISSION){        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (permissions[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)            &&grantResults[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] == PackageManager.PERMISSION_GRANTED){            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//用户同意使用write</span>            startGetImageThread();        }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//用户不同意,自行处理即可</span>            finish();        }    }}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>

  好了,这样就算是简单初步适配完成了。

处理不再提醒

  如果用户拒绝某授权。下一次弹框,用户会有一个“不再提醒”的选项的来防止app以后继续请求授权。 
  这里写图片描述 
  如果这个选项在拒绝授权前被用户勾选了。下次为这个权限请求requestPermissions时,对话框就不弹出来了,系统会直接回调onRequestPermissionsResult函数,回调结果为最后一次用户的选择。所以为了应对这种情况,系统提供了一个shouldShowRequestPermissionRationale()函数,这个函数的作用是帮助开发者找到需要向用户额外解释权限的情况,这个函数:

  1. 应用安装后第一次访问,直接返回false;
  2. 第一次请求权限时,用户拒绝了,下一次shouldShowRequestPermissionRationale()返回 true,这时候可以显示一些为什么需要这个权限的说明;
  3. 第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项时:shouldShowRequestPermissionRationale()返回 false;
  4. 设备的系统设置中禁止当前应用获取这个权限的授权,shouldShowRequestPermissionRationale()返回false;
  注意:第二次请求权限时,才会有“不再提醒”的选项,如果用户一直拒绝,并没有选择“不再提醒”的选项,下次请求权限时,会继续有“不再提醒”的选项,并且shouldShowRequestPermissionRationale()也会一直返回true。 
  所以利用这个函数我们可以进行相应的优化,针对shouldShowRequestPermissionRationale函数返回false的处理有两种方法:
  • 如果应用是第一次请求该权限,则直接调用requestPermissions函数去请求权限;如果不是则代表用户勾选了’不再提醒’,弹出dialog,告诉用户为什么你需要该权限,让用户自己手动开启该权限。链接:http://stackoverflow.com/questions/32347532/android-m-permissions-confused-on-the-usage-of-shouldshowrequestpermissionrati
  • 在onRequestPermissionsResult函数中进行检测,如果返回PERMISSION_DENIED,则去调用shouldShowRequestPermissionRationale函数,如果返回false代表用户已经禁止该权限(上面的3和4两种情况),弹出dialog告诉用户你需要该权限的理由,让用户手动打开。链接:http://stackoverflow.com/questions/30719047/android-m-check-runtime-permission-how-to-determine-if-the-user-checked-nev
  处理方法已经有了,修改一下代码,我这里就以第二种方案来处理了:

<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onRequestPermissionsResult</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> requestCode, String[] permissions, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>[] grantResults) {    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (requestCode == CODE_FOR_WRITE_PERMISSION){        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (permissions[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)            &&grantResults[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>] == PackageManager.PERMISSION_GRANTED){            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//用户同意使用write</span>            startGetImageThread();        }<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{            <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//用户不同意,向用户展示该权限作用</span>            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!ActivityCompat.shouldShowRequestPermissionRationale(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {                AlertDialog dialog = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> AlertDialog.Builder(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>)                        .setMessage(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"该相册需要赋予访问存储的权限,不开启将无法正常工作!"</span>)                        .setPositiveButton(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"确定"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DialogInterface.OnClickListener() {                            <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>                            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onClick</span>(DialogInterface dialog, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> which) {                                finish();                            }                        })                        .setNegativeButton(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"取消"</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DialogInterface.OnClickListener() {                            <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>                            <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onClick</span>(DialogInterface dialog, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> which) {                                finish();                            }                        }).create();                dialog.show();                <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span>;            }            finish();        }    }}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li></ul>

  当勾选不再提醒,并且拒绝之后,弹出dialog,提醒用户该权限的重要性: 
  这里写图片描述 
  搞定!!!

使用兼容库

  以上的代码在6.0版本上使用没有问题,但是在之前就有问题了,最简单粗暴的解决方法可能就是利用Build.VERSION.SDK_INT >= 23这个判断语句来判断了,方便的是SDK 23的v4包加入了专门类进行相关的处理:

  • ContextCompat.checkSelfPermission()被授权函数返回PERMISSION_GRANTED,否则返回PERMISSION_DENIED ,在所有版本都是如此。
  • ActivityCompat.requestPermissions()这个方法在6.0之前版本调用,OnRequestPermissionsResultCallback 直接被调用,带着正确的 PERMISSION_GRANTED或者PERMISSION_DENIED。
  • ActivityCompat.shouldShowRequestPermissionRationale()在6.0之前版本调用,永远返回false。
  用v4包的这三方法,完美兼容所有版本!下面是代码:

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">//使用兼容库就无需判断系统版本int hasWriteContactsPermission = ContextCompat<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.checkSelfPermission</span>(getApplication(), Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.WRITE</span>_EXTERNAL_STORAGE)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>if (hasWriteContactsPermission == PackageManager<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.PERMISSION</span>_GRANTED) {    startGetImageThread()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>}//需要弹出dialog让用户手动赋予权限else{    ActivityCompat<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.requestPermissions</span>(PickOrTakeImageActivity<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.this</span>, new String[]{Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.WRITE</span>_EXTERNAL_STORAGE}, CODE_FOR_WRITE_PERMISSION)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li></ul>

  onRequestPermissionsResult函数不变。后两个方法,我们也可以在Fragment中使用,用v13兼容包:FragmentCompat.requestPermissions() and FragmentCompat.shouldShowRequestPermissionRationale()和activity效果一样。

一次请求多个权限

  当然了有时候需要多个权限,可以用上面方法一次请求多个权限。当然最重要的是不要忘了为每个权限检查“不再提醒”的设置。

<code class="hljs avrasm has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">List<String> permissionsNeeded = new ArrayList<String>()<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>permissionsNeeded<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.add</span>(Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.ACCESS</span>_FINE_LOCATION)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>permissionsNeeded<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.add</span>(Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.READ</span>_CONTACTS)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>permissionsNeeded<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.add</span>(Manifest<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.permission</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.WRITE</span>_CONTACTS)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span>requestPermissions(permissionsNeeded<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.toArray</span>(new String[permissionsList<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">.size</span>()]), CODE_FOR_MULTIPLE_PERMISSION)<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">;</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>

  最后在onRequestPermissionsResult函数中一个个处理返回结果即可。

第三方库简化代码

  当然早就有第三方库来帮忙做这些事情了: 
  Github上的开源项目 PermissionHelper和hotchemi’s PermissionsDispatcher

APP处于运行状态下,被撤销权限

  如果APP正在运行中,用户进入设置-应用程序页面去手动撤销该APP权限,会出现什么情况呢?哈哈,系统又会接着弹出权限请求对话框,挺好挺好: 
  这里写图片描述 
  这样就没有问题了吧O(∩_∩)O~ 
  上面的测试环境为genymotion6.0模拟器,有朋友跟我反映在6.0nexus 6p真机上会直接退出应用,所以这个应该还和测试环境有关。

结论建议

  新运行时权限已经在棉花糖中被使用了。我们没有退路。我们现在唯一能做的就是保证app适配新权限模型。欣慰的是只有少数权限需要运行时权限模型。大多数常用的权限,例如,网络访问,属于Normal Permission 在安装时自动会授权,当然你要声明,以后无需检查。因此,只有少部分代码你需要修改。 
两个建议: 
  1.严肃对待新权限模型。 
  2.如果你代码没支持新权限,不要设置targetSdkVersion 23 。尤其是当你在Studio新建工程时,不要忘了修改! 
  说一下代码修改。这是大事,如果代码结构被设计的不够好,你需要一些很蛋疼的重构。每个app都要被修正。如上所说,我们没的选择。列出所有你需要请求权限的全部情形,如果A被授权,B被拒绝,会发生什么,针对每一个情况认真处理。

引用文章

http://www.jianshu.com/p/e1ab1a179fbb 
http://blog.csdn.net/yangqingqo/article/details/48371123 
http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en 
http://developer.android.com/training/permissions/requesting.html 
http://developer.android.com/guide/topics/security/permissions.html#normal-dangerous 
http://developer.android.com/reference/android/support/v4/app/ActivityCompat.html

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信被骗充话费怎么办 微信话费充多了怎么办 睫毛烫的太卷了怎么办 烫完睫毛太卷了怎么办 烫睫毛太卷了怎么办 用微信充话费充错了怎么办 微信给空号充话费了怎么办 微信充话费充错号码是空号怎么办 淘宝充流量不到账怎么办 微信退货不退款怎么办 京东话费交错号怎么办? 微信缴费错了怎么办 给手机充话费被退款怎么办 买到假货淘宝商家已关店怎么办 手机刷错系统了怎么办 苹果手机成砖了怎么办 苹果6p变砖头怎么办 苹果刷成石头了怎么办 苹果手机更新成了砖头怎么办 京东售后好慢怎么办 京东商品超过售后期怎么办 京东过了售后期怎么办 京东售后不处理怎么办 京东售后不让退货怎么办 天猫盒子遥控器丢了怎么办 淘宝店铺的客服不理人怎么办 淘宝假货下架了怎么办 淘宝不让发布本地生活服务了怎么办 淘宝删除差评后店家不返现怎么办 天猫店家迟迟不发货怎么办 淘宝下单后店家说缺货怎么办 用淘宝把话费冲到空号上怎么办 d速快递没有网点怎么办 京东买的货没收到怎么办 淘宝物流显示已揽件就是不动怎么办 淘宝查不到物流信息怎么办 快递物流信息更新错怎么办 淘宝上查不到物流怎么办 微信买的东西不给退怎么办 微信购物已收货怎么办 微信买东西不退怎么办