美文网首页
详解android6.0权限

详解android6.0权限

作者: 蛋西 | 来源:发表于2017-01-09 14:53 被阅读0次

    这是一篇迟到的文章。在6.0之前,在应用安装的时候,提示用户所需要用到的权限列表,同意之后安装,该app就被赋予所有的权限,我们暂且称它为安装时权限,安装后,被赋予的权限也无法取消,当然,国内的一些rom会在系统级别进行额外的一层权限管理,这个不在本文叙述范围之内;在6.0之后,google对权限进行了运行时的管理,而不是在安装时候,危险权限需要在运行时申请,我们暂且称它为运行时权限,非危险权限,在安装时由用户授予,这样简化了应用安装过程,因为用户在安装或更新应用时不需要授予权限,也给予了用户对app功能更多的控制

    权限分组

    系统权限主要分为两类,正常权限危险权限

    正常权限不会直接危及用户的隐私,如果你的应用在它的Manifest中列出了正常权限,系统会自动授予权限

    危险权限可以让app访问用户的机密数据,如果你的应用在它的Manifest列出了危险权限,用户必须明确批准你的app使用该权限

    当然,不管哪个版本的android,你应用中所用到的所有权限,不管是正常权限还是危险权限,都需要在应用的Manifest中申明

    如果你的设备运行Android 5.1以及5.1以下版本,或者你的应用的目标SDK是22以及22以下版本:如果你在应用的Manifest中申明了危险权限,用户在安装时必须授予权限,如果拒绝授予权限,那么系统就不会安装应用,也就是所谓的“一刀切”方式,不同意所有权限,就不能安装应用

    如果你的设备运行Android 6.0以及6.0以上版本,或者你的目标SDK是23以及23以上版本:应用必须在Manifest中罗列出所有的权限,并且在程序运行时,它必须请求用户授予每一个危险权限,此时用户可以授予或者拒绝每一个权限,并且应用程序可以继续运行有限的功能,即使用户拒绝了权限请求

    注意:从Android 6.0开始(API 23),用户可以在任何时候,对任何应用撤销权限,即使app申明的目标SDK低于23

    正常权限

    正常权限有以下几个特点

    1. 就是在安装时,由用户授予,后续运行时无需再次申请,无需再显示提醒用户,用户也不能取消这些权限(在Android 6.0及以上版本例外,用户可以通过管理界面撤销权限)

    2. 对用户隐私或安全没有较大影响

    正常权限列表

    • 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

    我们大概看一遍过去,可以总结归纳出正常权限有几类:蓝牙,网络状态,NFC,指纹,闹铃,快捷方式,震动等常用权限

    危险权限

    危险权限才是运行时权限的主要处理对象,因为这些权限可能引起隐私wenti或者影响其他程序运行,android中危险权限主要以组的形式出现,可以归纳为一下几个分组:

    • CALENDAR

    • CAMERA

    • CONTACTS

    • LOCATION

    • MICROPHONE

    • PHONE

    • SENSORS

    • SMS

    • STORAGE

    具体请看下图:

    android_permissions_group.jpg

    那么问题来了

    Q1.我们的应用是否必须支持运行时权限?

    Android为了应用兼容,实际上是可以不需要支持运行时权限的,只要设置targetSdkVersion低于23就可以了,意思是我的应用还乜有在API 23上面完全兼容,不要给我开启运行时权限新特性,当然了,早晚你还是要支持的,时间问题而已

    Q2.如果不支持运行时权限,应用会崩溃么?

    可能会奔溃,具体要根据你使用运行时权限在代码的什么地方,假设你应用启动就开始创建SDCard文件夹,但是在Android 6.0的设备上,系统或者用户在系统权限管理界面禁止了你的SDCard权限,如果应用代码处理不当,此时重新启动应用就会发生奔溃

    这里我们还要注意的一点是,危险权限是以组的方式授予的,怎么理解呢?按照我们上面列出的9大分组,举个栗子,如果你申请读取SDCard权限,在用户授予权限后,你自动就获得了写入SDCard的权限,也就是说你获得了STORAGE分组的所有权限

    运行时权限申请

    API使用

    申请运行时权限主要使用到的API有下面三个:

    
    # 检测系统当前是否被授予某个运行时权限,传入参数是权限名称,比如Manifest.permission.READ_EXTERNAL_STORAGE
    
    ContextCompat.checkSelfPermission(@NonNull Context context, @NonNull String permission)
    
    # 是否要显示权限说明
    
    # 1.用户第一次被拒绝(非永久拒绝)授予某个权限后,下次再次请求该权限,这个方法会返回true,用户有机会以某种方式对用户进行说明该权限用处
    
    # 2.用户在第一次拒绝某个权限后,下次再次申请时,授权的dialog中将会出现“不再提醒”选项,一旦选中勾选了,那么下次申请将不会提示用户。
    
    # 3.第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项,调用shouldShowRequestPermissionRationale()后返回false。
    
    # 4.设备的策略禁止当前应用获取这个权限的授权:shouldShowRequestPermissionRationale()返回false 。
    
    # 5.加这个提醒的好处在于,用户拒绝过一次权限后我们再次申请时可以提醒该权限的重要性,免得再次申请时用户勾选“不再提醒”并决绝,导致下次申请权限直接失败。
    
    ActivityCompat.shouldShowRequestPermissionRationale(@NonNull Activity activity,@NonNull String permission)
    
    # 请求权限授予,当然可以传入多个权限名称同时申请,用户会依次弹出多个提示框申请权限,App不能配置和修改这个对话框,如果需要提示用户这个权限相关的信息或说明,需要在调用 requestPermissions() 之前处理,该方法有两个参数:
    
    # int requestCode,会在回调onRequestPermissionsResult()时返回,用来判断是哪个授权申请的回调。
    
    # String[] permissions,权限数组,你需要申请的的权限的数组。
    
    # 由于该方法是异步的,所以无返回值,当用户处理完授权操作时,会回调Activity或者Fragment的onRequestPermissionsResult()方法。
    
    ActivityCompat.requestPermissions(final @NonNull Activity activity,final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode)
    
    # 用户拒绝或者授予用户权限后的回调方法,该方法在Activity/Fragment中应该被重写,当用户处理完授权操作时,系统会自动回调该方法,该方法有三个参数:
    
    # int requestCode,在调用requestPermissions()时的第一个参数。
    
    # String[] permissions,权限数组,在调用requestPermissions()时的第二个参数。
    
    # int[] grantResults,授权结果数组,对应permissions,具体值和上方提到的PackageManager中的两个常量做比较。
    
    onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
    
    

    我们以请求读写SDCard权限,来看一下一个标准的请求权限流程

    
    # 判断当前系统版本是都是6.0以上
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    
    # 检测是否已经被用户授予该权限
    
    if (ContextCompat.checkSelfPermission(LaunchActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=           PackageManager.PERMISSION_GRANTED) {
    
    # 没有被授予权限
    
    # 判断如果已经被用户拒绝过一次(非永久拒绝),则显示用户此权限的一些说明,这里可以用toast,当然也可以用自定义界面来显示给用户,如果是采用自定义界面来显示给用户,在有“确定”,“取消”等按钮的情况下,下面的“请求权限”步骤要酌情调用
    
    if (ActivityCompat.shouldShowRequestPermissionRationale(LaunchActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
    
    Toast.makeText(LaunchActivity.this, "request read external storage", Toast.LENGTH_LONG).show();
    
    }
    
    # 请求权限,数组传入多个值,可以一次请求多个权限
    
    ActivityCompat.requestPermissions(LaunchActivity.this,
    
    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
    
    PERMISSIONS_REQUEST_EXTERNAL_STORAGE);
    
    }
    
    } else {
    
    # 6.0以下版本,直接使用权限
    
    // 做一些权限对应的操作
    
    }
    
    

    如果是在6.0及6.0以上版本时,执行 ActivityCompat.requestPermissions()方法请求权限后,如果用户同意或者拒绝后,会回调onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)方法,该方法的几个参数,我们做一下解释:

    1. requestCode 对应的是ActivityCompat.requestPermissions请求时的requestCode,这个与Activity中常用的onActivityResult方法中的requestCode类似,用来标识某一次或者某一个请求的回调对应关系

    2. permissions 对应ActivityCompat.requestPermissions()方法中请求的权限列表

    3. grantResults 对应2中permissions的每个权限的用户应答结果

    好了,接下来就是根据requestCode,遍历permissions和grantResults中的结果,做对应的操作

    
    @Override
    
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    
    switch (requestCode) {
    
    case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
    
    // 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;
    
    }
    
    }
    
    }
    
    

    注意事项

    API问题

    由于checkSelfPermission和requestPermissions从API 23才加入,低于23版本,需要在运行时判断 或者使用Support Library v4中提供的方法

    • ContextCompat.checkSelfPermission

    • ActivityCompat.requestPermissions

    • ActivityCompat.shouldShowRequestPermissionRationale

    两个权限

    运行时权限对于应用影响比较大的权限有两个,他们分别是

    • READ_PHONE_STATE

    • WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE

    其中READ_PHONE_STATE用来获取deviceID,即IMEI号码。这是很多统计依赖计算设备唯一ID的参考。如果新的权限导致读取不到,避免导致统计的异常。建议在完全支持运行时权限之前,将对应的值写入到App本地数据中,对于新安装的,可以采取其他策略减少对统计的影响。

    WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE这两个权限和外置存储(即sdcard)有关,对于下载相关的应用这一点还是比较重要的,我们应该尽可能的说明和引导用户授予该权限。

    建议

    • 不要使用多余的权限,新增权限时要慎重

    • 使用Intent来替代某些权限,如拨打电话,选择图片(和你的产品经理PK去吧)

    • 对于使用权限获取的某些值,比如deviceId,尽量本地存储,下次访问直接使用本地的数据值

    注意,由于用户可以撤销某些权限,所以不要使用应用本地的标志位来记录是否获取到某权限,在使用的时候要遵循流程实时判断后使用,避免不正确的使用导致应用崩溃

    注意

    • 即使支持了运行时权限,也要在Manifest声明,因为市场应用会根据这个信息和硬件设备进行匹配,决定你的应用是否在该设备上显示。

    • 防止一次请求太多的权限或请求次数太多,用户可能对你的应用感到厌烦,在应用启动的时候,最好先请求应用必须的一些权限,非必须权限在使用的时候才请求,建议整理并按照上述分类管理自己的权限

    • 权限申请弹出的对话框不能自定义

    • 解释你的应用为什么需要这些权限:在你调用requestPermissions()之前,你为什么需要这个权限

    • 个人觉得Marshmallow的运行时权限对于用户来说绝对是一个好东西,但是目前想要支持需要做的事情还是比较多的。

    • 对于一个有很多依赖的宿主应用,想要做到支持还是有一些工作量的,因为你的权限申请受制于依赖。

    最后,通过以上使用例子的代码我们看到,虽然原理和内容很简单,但是流程上我们还是要写很多代码,判断几个条件,稍后我将在另外一片文章中根据请求运行时权限的特点,封装出一个库EPermission ,建议大家使用这个库,简化权限申请流程

    附录

    权限结果常量

    PackageManager.PERMISSION_DENIED:该权限是被拒绝的。

    PackageManager.PERMISSION_GRANTED:该权限是被授权的。

    权限名称

    Manifest.permission.READ_EXTERNAL_STORAGE

    Manifest.permission.CAMERA

    等等Manifest.permission类中对应的常量

    相关文章

      网友评论

          本文标题:详解android6.0权限

          本文链接:https://www.haomeiwen.com/subject/yvhhbttx.html