Android 6.0运行时权限简析及最佳实践

1、前言

从Android 6.0(API 23)开始,对系统权限做了很大的改变。在之前用户安装APP前,只是把APP需要使用的权限列出来给用户告知一下,APP安装后都可以访问这些权限。从6.0开始,一些敏感权限,需要在使用时动态申请,并且用户可以选择拒绝授权访问这些权限,已授予过的权限,用户也可以去APP设置页面去关闭授权。这对用户来说提高了安全性,可以防止一些应用恶意访问用户数据,但是对于开发来说,也增加了不少工作量,这块不做适配处理的话,APP在访问权限的时候会容易crash。

2、权限等级和权限组

权限主要分为normal、dangerous、signature和signatureOrSystem四个等级,常规情况下我们只需要了解前两种,即正常权限和危险权限。

2.1、正常权限

正常权限涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。应用声明其需要正常权限,系统会自动授予该权限。例如设置时区,只要应用声明过权限,系统就直接授予应用此权限。下面是截止到API 23的普通权限(需翻墙访问)

普通权限 普通权限
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_IGNORE_BATTERY_OPTIMIZATIONS 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
2.2、危险权限

危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如读取用户联系人,在6.0以上系统中,需要在运行时明确向用户申请权限。

2.3、权限组

系统根据权限用途又定义了权限组,每个权限都可属于一个权限组,每个权限组可以包含多个权限。例如联系人权限组,包含读取联系人、修改联系人和获取账户三个权限。

* 如果应用申请访问一个危险权限,而此应用目前没有对应的权限组内的任何权限,系统会弹窗提示用户要访问的权限组(注意不是权限)。例如无论你申请READ_CONTACTS还是WRITE_CONTACTS,都是提示应用需要访问联系人信息。

* 如果用户申请访问一个危险权限,而应用已经授权同权限组的其他权限,则系统会直接授权,不会再与用户有交互。例如应用已经请求并授予了READ_CONTACTS权限,那么当应用申请WRITE_CONTACTS时,系统会立即授予该权限。下面为危险权限和权限组:

权限组 权限
CALENDAR READ_CALENDAR
WRITE_CALENDAR
CAMERA CAMERA
CONTACTS READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONE RECORD_AUDIO
PHONE READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORS BODY_SENSORS
SMS SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

3、运行时请求权限

关于运行时请求权限,官网介绍的很清楚,也有很多其他文章介绍,这里只是简单罗列一下。

3.1、检查权限

应用每次需要危险权限时,都要判断应用目前是否有该权限。兼容库中已经做了封装,只需要通过下面代码即可:

int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

如果有权限则返回PackageManager.PERMISSION_GRANTED,否则返回PackageManager。PERMISSION_DENIED。

3.2、请求权限

当应用需要某个权限时,可以申请获取权限,这时会有弹出一个系统标准Dialog提示申请权限,此Diolog不能定制,用户同意或者拒绝后会通过方法onRequestPermissionsResult()返回结果。当用户拒绝过此权限申请时,再次申请Dialog上可以勾选不再提示,这种情况下,以后再申请权限不会弹Dialog直接返回拒绝。所以一些依赖某些敏感权限的应用,需要自己去处理,向用户解释 为什么需要此权限,说服用户授予权限。请求权限代码如下:

ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS}, REQUEST_CODE);
3.3、处理权限请求响应

当用户处理权限请求后,系统会回调申请权限的Activity的onRequestPermissionsResult()方法,只需要覆盖此方法,就能获得返回结果

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }
    }
}
3.4、考虑使用intent

有很多权限操作可以考虑调用其他应用,这样的话当前应用就不需要申请权限。例如想要获取相机照相,可以通过ACTION_IMAGE_CAPTURE唤起相机应用去完成,相机应用会把照片返回。同样拨打电话、访问联系人,都可以考虑使用类似方法。相比较其他应用,这类专门的应用做一些操作更容易让用户接受。

4、 关于国产机6.0以下系统

部分国产厂商定制了系统(例如小米、华为),在6.0以下系统就可以单独控制权限。但是通常它们处理的不够彻底,代码中判断是否授权的时候,返回的是已经授权。而真正去做操作的时候,却会因为没有权限导致应用crash。例如我曾遇到过的场景:

* 访问联系人,可以拿到Cursor对象,但是cursor.moveToFirst()会返回false。

* 访问摄像头时,获取到的Camera对象为空

所以在低版本手机上,不要以为拥有系统授权就万事大吉了,一定要多加条件判读和测试。

5、PermissionGrantor,一行代码搞定动态权限申请。

上面运行时请求权限中,我们看到了权限申请依赖于Activity和Fragment,和startActivityForResult()方法的使用类似,必须依赖于Activity和Fragment的回调方法。正常情况下还能满足需求,可是当申请的权限的代码在一个独立的模块中时,例如我封装了一个UI控件,控件中某个操作需要申请权限,或者项目采用了MVVM框架,需要在一些view类或者model类中申请权限,这是处理起来就会很麻烦。

我在自己代码中就遇到类似情况,后面我采用了使用一个单独的Activity来申请权限,通过回调的方式通知业务层授权结果。感觉使用起来挺方便,就把它放到maven仓库中了,项目中通过加入如下依赖即可。

compile ‘com.github.dfqin:grantor:1.0.1‘
5.1 PermissionGrantor使用

申请权限很简单,只需要下面一句话即可。

PermissionsUtil.requestPermission(Activity activity, PermissionListener listener,
    String[] permissions);

下面是一个申请摄像头的例子:

private void requestCemera() {
        if (PermissionsUtil.hasPermission(this, Manifest.permission.CAMERA)) {
            //有访问摄像头的权限
        } else {
            PermissionsUtil.requestPermission(this, new PermissionListener() {
                @Override
                public void permissionGranted(@NonNull String[] permissions) {
                   //用户授予了访问摄像头的权限
                }

                @Override
                public void permissionDenied(@NonNull String[] permissions) {
                    //用户拒绝了访问摄像头的申请
                }
            }, new String[]{Manifest.permission.CAMERA});
        }
    }

PermissionsUtil.requestPermission还有两个重载的实现。用户拒绝授权时,会有一个默认Dialog提示用户开通权限。这个Dialog的内容可以定制,也可以不显示此Dialog,详情见Github。有什么问题欢迎讨论交流。

6、参考

时间: 2024-10-16 16:17:08

Android 6.0运行时权限简析及最佳实践的相关文章

Android 6.0 运行时权限管理最佳实践

Android 6.0 运行时权限管理最佳实践 版权声明:转载必须注明本文转自严振杰的博客: http://blog.yanzhenjie.com 这是一篇迟来的博客,Android M已经发布一年多了(6.0的变化),在Android M中权限系统被重新设计,发生了颠覆性的变化,很多人把握不好这个变化,一是对这个权限策略和套路还没有摸透,二是没有一个很好的实践来支撑,在我的技术开发群里很多人问我关于权限的问题,往往我都没有直接回答,因为这个问题不是一两句说的清楚的,这几点是今天我写这篇博客的原

1.Android6.0运行时权限简介_2.Android6.0权限适配之WRITE_EXTERNAL_STORAGE(SD卡写入)3_.Android 6.0 运行时权限理解

Android6.0运行时权限简介:http://unclechen.github.io/2016/03/05/Android6.0%E8%BF%90%E8%A1%8C%E6%97%B6%E6%9D%83%E9%99%90%E7%AE%80%E4%BB%8B/ Android6.0权限适配之WRITE_EXTERNAL_STORAGE(SD卡写入):https://blog.csdn.net/xiechengfa/article/details/52699233 Android 6.0 运行时权

Android 6.0+ 运行时权限

1.权限被分为了普通和危险两种 2.打电话的Demo import android.Manifest; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import andro

Android 6.0 运行时权限处理完全解析

一.概述 随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化.对于6.0的几个主要的变化,查看查看官网的这篇文章http://developer.android.com/intl/zh-cn/about/versions/marshmallow/android-6.0-changes.html,其中当然包含Runtime Permissions. ok,本篇文章目的之一就是对运行时权限处理的一个介绍,以及对目前权限相关的库

Android 6.0 运行时权限管理

android 6.0 对权限进行了严格的管理 新的权限策略讲权限分为两类,第一类是不涉及用户隐私的,只需要在Manifest中声明即可,比如网络.蓝牙.NFC等:第二类是涉及到用户隐私信息的,需要用户授权后才可使用,比如SD卡读写.联系人.短信读写等. Normal Permissions 此类权限都是正常保护的权限,只需要在AndroidManifest.xml中简单声明这些权限即可,安装即授权,不需要每次使用时都检查权限,而且用户不能取消以上授权,除非用户卸载App. ACCESS_LOC

Android 6.0 运行时权限处理

运行时权限介绍 Android 6.0在我们原有的AndroidManifest.xml声明权限的基础上,又新增了运行时权限动态检测,以下权限都需要在运行时判断: 身体传感器 日历 摄像头 通讯录 地理位置 麦克风 电话 短信 存储空间 运行时权限处理 Android6.0系统默认为targetSdkVersion小于23的应用默认授予了所申请的所有权限,所以如果你以前的APP设置的targetSdkVersion低于23,在运行时也不会崩溃,但这也只是一个临时的救急策略,用户还是可以在设置中取

Android数据存储之Android 6.0运行时权限下文件存储的思考

前言: 在我们做App开发的过程中基本上都会用到文件存储,所以文件存储对于我们来说是相当熟悉了,不过自从Android 6.0发布之后,基于运行时权限机制访问外置sdcard是需要动态申请权限,所以以往直接sdcard根目录上直接新建了一个xxx/cache/目录来做文件存储就会不是那么容易控制了,所以有必要重新认识一下Android文件存储的相关知识了. 背景: 有关外置sdcard的读写权限 <uses-permission android:name="android.permissi

Android 6.0+ 运行时权限探索

原创文章,转载请注明 ( 来自:http://blog.csdn.net/leejizhou/article/details/51511630 李济洲的博客 ) 引言:去年Android 6.0发布后,其新引入的(Requesting Permissions at Run Time)运行时权限就备受开发者关注,随着今年国内手机厂商对6.0系统的普及,觉得大家有必要了解下这个新特性,因为在TargetSDK23+进行开发不注意这些会造成APP运行在6.0+手机上崩溃,这篇博文将对这个新特性进行探索

谈谈Android 6.0运行时权限理解

前言 谷歌在2015年8月份时候,发布了Android 6.0版本,代号叫做“棉花糖”(Marshmallow ),其中的很大的一部分变化,是在用户权限授权上,或许是感觉之前默认授权的不合理,现在6.0出来,使得用户权限授权变得合理.这可能也是参考IOS系统的,只有在用户需要使用权限的时候,才去授权请求,这样做的目的是提高用户体验,当然,用户感觉好了,受苦的是我们开发人员,原来的规则不适用了,现在我们去适应新的规则,毕竟是靠谷歌这颗大树吃饭的嘛. 原来权限模型 在Android 6.0版本之前,