• 请加作者QQ或者微信联系。作者QQ:524100248,微信号:sendtion。

Android6.0运行时权限原生实现和MIUI下的处理

安卓教程 sendtion 1年前 (2017-04-01) 3333次浏览 已收录 3个评论 扫描二维码

1、前言

自从 Android6.0 发布,增加了许多新的特性和功能,除了强化和完善了 MD 设计元素,Android的安全也得到了谷歌的重视。于是,Android6.0 中出现了运行时权限的概念。许多程序员前赴后继,推出了大量的优秀的第三方库,来简化运行时权限的使用。但是,我觉得我们有必要从根本上学会使用运行时权限的申请,这能增加我们对 Permission 的理解。
本教程代码:https://github.com/sendtion/SDCPermission


2、运行时权限

当我们的 targetSdkVersion>=23 时,我们一定要注意权限的申请,否则 App 会因为没有获取权限而发生崩溃。当然,在 API 23 以下系统版本也可以判断一下权限是否得到,确保万无一失。
我们不能连续一次性申请多个权限,这会使用户感觉到不安全,感觉 App 有窃取隐私信息的可能,可能因此丧失一部分用户。也不能在 App 内到处申请权限,这会是用户感到烦躁和麻烦,这也会丧失一部分用户。

所以,我们申请权限的时候一定要注意一些原则:

a>尽量精简权限,剔除不必要的权限
大量的权限,只会让用户丧失安全感或者感到烦躁,得不偿失。

b>用户拒绝后再次请求一定要详细说明缘由
给用户详细说明,我们需要这个权限来干什么,用户才更容易接受。

c>在合理的位置申请权限
不要一次性申请所有的权限,这中感觉很不好,会让用户感觉自己被强 X 了。


3、系统权限分类

Google 将权限分为两类,一类是普通权限(Normal Permissions),这类权限一般不涉及用户隐私,也不需要用户进行授权,如网络访问,手机震动等,这些权限如下所示:

<code >ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS</code>

另外一类是危险权限(Dangerous Permission),涉及用户隐私,需要用户授权,如对 sd 卡读取、访问用户手机通讯录等。如下所示:

<code class="hljs avrasm has-numbering">android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.CALENDAR</span>(日历数据) 
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.READ</span>_CALENDAR
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.WRITE</span>_CALENDAR

android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.CAMERA</span>(相机) 
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.CAMERA</span>

android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.CONTACTS</span>(联系人)  
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.READ</span>_CONTACTS
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.WRITE</span>_CONTACTS
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.GET</span>_ACCOUNTS

android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.LOCATION</span>(位置)   
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.ACCESS</span>_FINE_LOCATION
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.ACCESS</span>_COARSE_LOCATION

android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.MICROPHONE</span>(麦克风)    
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.RECORD</span>_AUDIO

android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.PHONE</span>(电话)  
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.READ</span>_PHONE_STATE
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.CALL</span>_PHONE
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.READ</span>_CALL_LOG
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.WRITE</span>_CALL_LOG
<span class="hljs-keyword">com</span><span class="hljs-preprocessor">.android</span><span class="hljs-preprocessor">.voicemail</span><span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.ADD</span>_VOICEMAIL
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.USE</span>_SIP
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.PROCESS</span>_OUTGOING_CALLS

android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.SENSORS</span>(传感器)   
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.BODY</span>_SENSORS

android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.SMS</span>(短信)    
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.SEND</span>_SMS
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.RECEIVE</span>_SMS
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.READ</span>_SMS
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.RECEIVE</span>_WAP_PUSH
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.RECEIVE</span>_MMS
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.READ</span>_CELL_BROADCASTS

android<span class="hljs-preprocessor">.permission</span>-group<span class="hljs-preprocessor">.STORAGE</span>(存储)    
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.READ</span>_EXTERNAL_STORAGE
android<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.WRITE</span>_EXTERNAL_STORAGE</code>

查看 Dangerous Permission 可以发现权限是分组的,这个和 Android 6.0 的授权机制有关。如果你申请某个危险的权限,假设你的 app 早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的 app 对 READ_CONTACTS 已经授权了,当你的 app 申请 WRITE_CONTACTS 时,系统会直接授权通过。此外,申请时弹出的 dialog 上面的文本说明也是对整个权限组的说明,而不是单个权限(ps:这个 dialog 是不能进行定制的)。

注:不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。


4、权限申请方式

以请求读取联系人权限为例:
a> 在 AndroidManifest.xml 文件中声明所需要的权限
b> 检查是否授予了所需要的权限

<code class="hljs avrasm has-numbering">ContextCompat<span class="hljs-preprocessor">.checkSelfPermission</span>(this,Manifest<span class="hljs-preprocessor">.permission</span><span class="hljs-preprocessor">.READ</span>_CONTACTS) != PackageManager<span class="hljs-preprocessor">.PERMISSION</span>_GRANTED</code>

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

c> 如果没有授予权限,则请求授权

<code class="hljs javascript has-numbering">ActivityCompat.requestPermissions(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> <span class="hljs-built_in">String</span>[]{Manifest.permission.READ_CONTACTS},code);</code>

该方法是异步的,第一个参数是 Context;第二个参数是需要申请的权限的字符串数组;第三个参数为 requestCode,主要用于回调的时候检测。可以从方法名 requestPermissions 以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。

d> 处理权限申请的结果

<code class="hljs java has-numbering"><span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onRequestPermissionsResult</span>(<span class="hljs-keyword">int</span> requestCode, @NonNull String[] permissions, @NonNull <span class="hljs-keyword">int</span>[] grantResults) {
     <span class="hljs-keyword">super</span>.onRequestPermissionsResult(requestCode, permissions, grantResults);
     <span class="hljs-comment">//TODO</span>
     <span class="hljs-keyword">if</span>(grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
     <span class="hljs-comment">//获得了权限</span>
     } <span class="hljs-keyword">else</span> {
     <span class="hljs-comment">//拒绝了权限</span>
     }
}</code>

e> 如果用户拒绝了,下次申请给予解释

<code class="hljs cs has-numbering">ActivityCompat.shouldShowRequestPermissionRationale(<span class="hljs-keyword">this</span>, permission)</code>

这个 API 主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。

注:
* 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
* 如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。
* 如果设备规范禁止应用具有该权限,此方法也会返回 false。


5、优化

到这里算是处理完了,这么看起来并没有多麻烦,但是坑还是存在的。Android 碎片化严重,大家都知道。国产的各种定制系统深度修改,好多底层 API 都有所改变,给我们 Android 开发者带来了极大的麻烦。诸如 MIUI、Flyme、EMUI、FuntouchOS、ColorOS、SmartisanOS、CoolUI、H2OS 等等等,各种各样,五花八门。

本教程的例子用的是小米 5 MIUI8 7.0 开发版,就遇到了很坑的事情。
小米默认是允许读写存储卡权限的,其他的权限都是询问状态。
这里写图片描述

测试
默认询问,除了日历、录音、相机、存储卡会弹窗,你可以选择拒绝或者允许;其他权限不弹窗,checkSelfPermission 直接返回 0,也就是直接允许了,此时权限状态仍然为询问状态;
手动拒绝后不会再弹窗,相当于选中了不再询问,权限状态由询问变为拒绝,需要手动给予权限;
手动允许后,权限状态由询问变为允许;
MIUI 上:shouldShowRequestPermissionRationale()返回一直是 false,无法进行判断解释。

GitHub 地址:https://github.com/sendtion/SDCPermission
完整的代码如下:

<code class="hljs java has-numbering"><span class="hljs-keyword">package</span> com.sdc.sdcpermission;

<span class="hljs-keyword">import</span> android.Manifest;
<span class="hljs-keyword">import</span> android.content.DialogInterface;
<span class="hljs-keyword">import</span> android.content.Intent;
<span class="hljs-keyword">import</span> android.content.pm.PackageManager;
<span class="hljs-keyword">import</span> android.net.Uri;
<span class="hljs-keyword">import</span> android.os.Build;
<span class="hljs-keyword">import</span> android.os.Bundle;
<span class="hljs-keyword">import</span> android.support.annotation.NonNull;
<span class="hljs-keyword">import</span> android.support.v4.app.ActivityCompat;
<span class="hljs-keyword">import</span> android.support.v4.content.ContextCompat;
<span class="hljs-keyword">import</span> android.support.v7.app.AlertDialog;
<span class="hljs-keyword">import</span> android.support.v7.app.AppCompatActivity;
<span class="hljs-keyword">import</span> android.support.v7.widget.Toolbar;
<span class="hljs-keyword">import</span> android.view.Menu;
<span class="hljs-keyword">import</span> android.view.MenuItem;
<span class="hljs-keyword">import</span> android.view.View;
<span class="hljs-keyword">import</span> android.widget.TextView;
<span class="hljs-keyword">import</span> android.widget.Toast;

<span class="hljs-javadoc">/**
 * Android 6.0 原生系统运行时权限适配
 * 在联想乐檬 X3 测试通过
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MainActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppCompatActivity</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">View</span>.<span class="hljs-title">OnClickListener</span> {</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String TAG = <span class="hljs-string">"MainActivity"</span>;<span class="hljs-comment">//快捷键 logt</span>

    <span class="hljs-keyword">private</span> TextView tv_sdcard;
    <span class="hljs-keyword">private</span> TextView tv_sms;
    <span class="hljs-keyword">private</span> TextView tv_phone;
    <span class="hljs-keyword">private</span> TextView tv_contacts;
    <span class="hljs-keyword">private</span> TextView tv_location;
    <span class="hljs-keyword">private</span> TextView tv_record;
    <span class="hljs-keyword">private</span> TextView tv_camera;
    <span class="hljs-keyword">private</span> TextView tv_calender;
    <span class="hljs-keyword">private</span> TextView tv_sensor;

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(Bundle savedInstanceState) {
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">initViews</span>() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        tv_contacts = (TextView) findViewById(R.id.tv_contacts);
        tv_sms = (TextView) findViewById(R.id.tv_sms);
        tv_phone = (TextView) findViewById(R.id.tv_phone);
        tv_location = (TextView) findViewById(R.id.tv_location);
        tv_sdcard = (TextView) findViewById(R.id.tv_sdcard);
        tv_record = (TextView) findViewById(R.id.tv_record);
        tv_calender = (TextView) findViewById(R.id.tv_calender);
        tv_camera = (TextView) findViewById(R.id.tv_camera);
        tv_sensor = (TextView) findViewById(R.id.tv_sensor);

        tv_contacts.setOnClickListener(<span class="hljs-keyword">this</span>);
        tv_sms.setOnClickListener(<span class="hljs-keyword">this</span>);
        tv_phone.setOnClickListener(<span class="hljs-keyword">this</span>);
        tv_location.setOnClickListener(<span class="hljs-keyword">this</span>);
        tv_sdcard.setOnClickListener(<span class="hljs-keyword">this</span>);
        tv_record.setOnClickListener(<span class="hljs-keyword">this</span>);
        tv_calender.setOnClickListener(<span class="hljs-keyword">this</span>);
        tv_camera.setOnClickListener(<span class="hljs-keyword">this</span>);
        tv_sensor.setOnClickListener(<span class="hljs-keyword">this</span>);

    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onCreateOptionsMenu</span>(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">onOptionsItemSelected</span>(MenuItem item) {
        <span class="hljs-keyword">int</span> id = item.getItemId();
        <span class="hljs-keyword">if</span> (id == R.id.action_miui) {
            <span class="hljs-comment">//跳转到 MIUI 的权限适配界面</span>
            Intent intent = <span class="hljs-keyword">new</span> Intent();
            intent.setClass(<span class="hljs-keyword">this</span>, MIUIActivity.class);
            startActivity(intent);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.onOptionsItemSelected(item);
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">showToast</span>(String text){
        Toast.makeText(<span class="hljs-keyword">this</span>, text, Toast.LENGTH_SHORT).show();
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(View v) {
        <span class="hljs-keyword">switch</span> (v.getId()){
            <span class="hljs-keyword">case</span> R.id.tv_contacts:
                <span class="hljs-comment">//READ_CONTACTS 是读取联系人</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.READ_CONTACTS) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>
                    <span class="hljs-comment">//如果没有权限则申请权限</span>
                    showTipsDialog(<span class="hljs-string">"读取联系人"</span>, Manifest.permission.READ_CONTACTS, <span class="hljs-number">101</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘读取联系人’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> R.id.tv_location:
                <span class="hljs-comment">//ACCESS_FINE_LOCATION 是 GPS 定位,ACCESS_COARSE_LOCATION 是网络定位</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.ACCESS_FINE_LOCATION) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>

                    showTipsDialog(<span class="hljs-string">"GPS 定位"</span>, Manifest.permission.ACCESS_FINE_LOCATION, <span class="hljs-number">102</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘GPS 定位’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> R.id.tv_sdcard:
                <span class="hljs-comment">//READ_EXTERNAL_STORAGE 是读取存储卡,</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.READ_EXTERNAL_STORAGE) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>

                    showTipsDialog(<span class="hljs-string">"读存储卡"</span>, Manifest.permission.READ_EXTERNAL_STORAGE, <span class="hljs-number">103</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘读存储卡’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> R.id.tv_sms:
                <span class="hljs-comment">//READ_SMS 是读取短信,SEND_SMS 是发送短信</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.READ_SMS) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>

                    showTipsDialog(<span class="hljs-string">"读取短信"</span>, Manifest.permission.READ_SMS, <span class="hljs-number">104</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘读取短信’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> R.id.tv_phone:
                <span class="hljs-comment">//CALL_PHONE 是拨打电话</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.CALL_PHONE) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>

                    showTipsDialog(<span class="hljs-string">"拨打电话"</span>, Manifest.permission.CALL_PHONE, <span class="hljs-number">105</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘拨打电话’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> R.id.tv_record:
                <span class="hljs-comment">//RECORD_AUDIO 是录音</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.RECORD_AUDIO) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>

                    showTipsDialog(<span class="hljs-string">"录音"</span>, Manifest.permission.RECORD_AUDIO, <span class="hljs-number">106</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘录音’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> R.id.tv_camera:
                <span class="hljs-comment">//CAMERA 是调用相机</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.CAMERA) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>

                    showTipsDialog(<span class="hljs-string">"调用相机"</span>, Manifest.permission.CAMERA, <span class="hljs-number">107</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘调用相机’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> R.id.tv_calender:
                <span class="hljs-comment">//WRITE_CALENDAR 是写日历</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.WRITE_CALENDAR) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>

                    showTipsDialog(<span class="hljs-string">"写日历"</span>, Manifest.permission.WRITE_CALENDAR, <span class="hljs-number">108</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘写日历’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> R.id.tv_sensor:
                <span class="hljs-comment">//BODY_SENSORS 是传感器权限</span>
                <span class="hljs-keyword">if</span> (ContextCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.BODY_SENSORS) !=
                        PackageManager.PERMISSION_GRANTED){<span class="hljs-comment">//拒绝了权限,或者没有获得权限</span>

                    showTipsDialog(<span class="hljs-string">"传感器"</span>, Manifest.permission.BODY_SENSORS, <span class="hljs-number">109</span>);
                } <span class="hljs-keyword">else</span> {
                    showToast(<span class="hljs-string">"已经获得‘传感器’权限"</span>);
                }
                <span class="hljs-keyword">break</span>;
        }
    }

    <span class="hljs-javadoc">/**
     * ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.READ_CONTACTS)
     * 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
     * 如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。
     * 如果设备规范禁止应用具有该权限,此方法也会返回 false。
     *<span class="hljs-javadoctag"> @param</span> requestCode
     *<span class="hljs-javadoctag"> @param</span> permissions
     *<span class="hljs-javadoctag"> @param</span> grantResults
     */</span>
    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onRequestPermissionsResult</span>(<span class="hljs-keyword">int</span> requestCode, @NonNull String[] permissions, @NonNull <span class="hljs-keyword">int</span>[] grantResults) {
        <span class="hljs-keyword">super</span>.onRequestPermissionsResult(requestCode, permissions, grantResults);
        <span class="hljs-keyword">switch</span> (requestCode){
            <span class="hljs-keyword">case</span> <span class="hljs-number">101</span>:
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    showToast(<span class="hljs-string">"已经获得‘读取联系人’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘读取联系人’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"读取联系人"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">102</span>:
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    <span class="hljs-comment">//获取了权限</span>
                    showToast(<span class="hljs-string">"已经获得‘GPS 定位’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘GPS 定位’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"GPS 定位"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">103</span>:
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    <span class="hljs-comment">//获取了权限</span>
                    showToast(<span class="hljs-string">"已经获得‘读存储卡’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘读存储卡’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"读存储卡"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">104</span>:
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    <span class="hljs-comment">//获取了权限</span>
                    showToast(<span class="hljs-string">"已经获得‘读取短信’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘读取短信’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"读取短信"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">105</span>:
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    <span class="hljs-comment">//获取了权限</span>
                    showToast(<span class="hljs-string">"已经获得‘拨打电话’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘拨打电话’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"拨打电话"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">106</span>:
                <span class="hljs-comment">//录音默认询问,请求权限会弹窗</span>
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    <span class="hljs-comment">//获取了权限</span>
                    showToast(<span class="hljs-string">"已经获得‘录音’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘录音’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"录音"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">107</span>:
                <span class="hljs-comment">//相机默认询问,请求权限会弹窗</span>
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    <span class="hljs-comment">//获取了权限</span>
                    showToast(<span class="hljs-string">"已经获得‘调用相机’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘调用相机’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"调用相机"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">108</span>:
                <span class="hljs-comment">//日历默认询问,请求权限会弹窗</span>
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    <span class="hljs-comment">//获取了权限</span>
                    showToast(<span class="hljs-string">"已经获得‘写日历’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘写日历’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"写日历"</span>);
                }
                <span class="hljs-keyword">break</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">109</span>:
                <span class="hljs-comment">//传感器默认允许,不会弹窗</span>
                <span class="hljs-keyword">if</span> (grantResults.length > <span class="hljs-number">0</span> && grantResults[<span class="hljs-number">0</span>] == PackageManager.PERMISSION_GRANTED){
                    <span class="hljs-comment">//获取了权限</span>
                    showToast(<span class="hljs-string">"已经获得‘传感器’权限???"</span>);
                } <span class="hljs-keyword">else</span> {
                    <span class="hljs-comment">//拒绝了权限</span>
                    showToast(<span class="hljs-string">"拒绝获得‘传感器’权限"</span>);
                    getAppDetailSettingIntent(<span class="hljs-string">"传感器"</span>);
                }
                <span class="hljs-keyword">break</span>;
        }
    }

    <span class="hljs-javadoc">/**
     * 显示对话框,提示用户允许权限
     *<span class="hljs-javadoctag"> @param</span> name
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">showTipsDialog</span>(String name, <span class="hljs-keyword">final</span> String permission, <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> code){
        <span class="hljs-keyword">if</span> (ActivityCompat.shouldShowRequestPermissionRationale(<span class="hljs-keyword">this</span>, permission)){
            AlertDialog.Builder builder = <span class="hljs-keyword">new</span> AlertDialog.Builder(<span class="hljs-keyword">this</span>);
            builder.setTitle(<span class="hljs-string">"权限申请提示"</span>);
            builder.setMessage(<span class="hljs-string">"当前应用缺少"</span>+name+<span class="hljs-string">"权限。是否立即申请权限?"</span>);
            builder.setNegativeButton(<span class="hljs-string">"取消"</span>, <span class="hljs-keyword">null</span>);
            builder.setPositiveButton(<span class="hljs-string">"确定"</span>, <span class="hljs-keyword">new</span> DialogInterface.OnClickListener() {
                <span class="hljs-annotation">@Override</span>
                <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(DialogInterface dialog, <span class="hljs-keyword">int</span> which) {
                    ActivityCompat.requestPermissions(MainActivity.<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> String[]{
                            permission},code);
                }
            });
            builder.create().show();
        } <span class="hljs-keyword">else</span> {
            showToast(<span class="hljs-string">"我们需要"</span>+name+<span class="hljs-string">"权限,给我吧!"</span>);
            <span class="hljs-comment">//拒绝后不再询问选中的话,下面就不会在执行</span>
            <span class="hljs-comment">//如果选中不再询问,则询问状态会变成拒绝,否则一直是询问状态</span>
            ActivityCompat.requestPermissions(MainActivity.<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> String[]{
                    permission},code);
        }
    }

    <span class="hljs-javadoc">/**
     * 跳转到 App 详情页
     */</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">getAppDetailSettingIntent</span>(String name) {
        AlertDialog.Builder builder = <span class="hljs-keyword">new</span> AlertDialog.Builder(<span class="hljs-keyword">this</span>);
        builder.setTitle(<span class="hljs-string">"权限申请提示"</span>);
        builder.setMessage(<span class="hljs-string">"当前应用缺少"</span>+name+<span class="hljs-string">"权限。请到应用信息——>权限管理中手动给予权限。"</span>);
        builder.setNegativeButton(<span class="hljs-string">"取消"</span>, <span class="hljs-keyword">null</span>);
        builder.setPositiveButton(<span class="hljs-string">"设置"</span>, <span class="hljs-keyword">new</span> DialogInterface.OnClickListener() {
            <span class="hljs-annotation">@Override</span>
            <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onClick</span>(DialogInterface dialog, <span class="hljs-keyword">int</span> which) {
                Intent localIntent = <span class="hljs-keyword">new</span> Intent();
                localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                <span class="hljs-keyword">if</span> (Build.VERSION.SDK_INT >= <span class="hljs-number">9</span>) {
                    localIntent.setAction(<span class="hljs-string">"android.settings.APPLICATION_DETAILS_SETTINGS"</span>);
                    localIntent.setData(Uri.fromParts(<span class="hljs-string">"package"</span>, getPackageName(), <span class="hljs-keyword">null</span>));
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Build.VERSION.SDK_INT <= <span class="hljs-number">8</span>) {
                    localIntent.setAction(Intent.ACTION_VIEW);
                    localIntent.setClassName(<span class="hljs-string">"com.android.settings"</span>,<span class="hljs-string">"com.android.settings.InstalledAppDetails"</span>);
                    localIntent.putExtra(<span class="hljs-string">"com.android.settings.ApplicationPkgName"</span>, getPackageName());
                }
                startActivity(localIntent);
            }
        });
        builder.create().show();
    }

}
</code>

6、第三方库推荐

第三方库推荐,列举的有点多,至于你喜欢哪个,需要自己去发现~
前三个都不错,个人用的第四个!

yanzhenjie/AndPermission: Android permission.
https://github.com/yanzhenjie/AndPermission (严振杰)
hongyangAndroid/MPermissions: a easy API to use runtime permission for Android M
https://github.com/hongyangAndroid/MPermissions (鸿洋)
lovedise/PermissionGen: Android API easy to use permission for Android M
https://github.com/lovedise/PermissionGen
tbruyelle/RxPermissions: Android runtime permissions powered by RxJava 支持 RxJava 的动态权限
https://github.com/tbruyelle/RxPermissions
forJrking/HeiPermission: Hei 一句代码搞定 Android M 动态权限检测。信不信由你,反正我用了。
https://github.com/forJrking/HeiPermission
rebus007/PermissionUtils: Check marshmallow permission easily
https://github.com/rebus007/PermissionUtils
mylhyl/AndroidAcp: 一句话搞定,简化 Android 6.0 系统复杂的权限操作
https://github.com/mylhyl/AndroidAcp
lypeer/FcPermissions: Fuck the permissions in Android M
https://github.com/lypeer/FcPermissions
Airsaid/MPermissionUtils: Android6.0 运行时权限 超轻量级工具类
https://github.com/Airsaid/MPermissionUtils
AndSync/XPermissionUtils: 可能是最精简的 Android6.0 运行时权限处理方式
https://github.com/AndSync/XPermissionUtils

如有疑问,欢迎指正!

个人简介:
GitHub:https://github.com/sendtion
博客:http://sendtion.cn/
简书:http://www.jianshu.com/users/630d98ed1424


乐趣公园 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:Android6.0 运行时权限原生实现和 MIUI 下的处理
喜欢 (4)
[sendtion@163.com]
分享 (0)
sendtion
关于作者:
一个不断奋斗追逐梦想的少年~
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(3)个小伙伴在吐槽
  1. 6b5c8d0b52cc8aeccd49a0c88326b7e4风的记忆
    吹雪2018-01-18 21:35 回复 未知操作系统 | 未知浏览器
  2. 哟呵,不错
    lgjdhz2017-05-05 16:12 回复 未知操作系统 | 360浏览器 1
    • sendtion
      感谢支持!
      sendtion2017-05-05 16:15 回复 Windows 10 | Chrome 49.0.2623.75