Android 6.0 运行时权限实践要点

Android在6.0之后添加了新的权限管理模型,每个应用必须在原油的Manifest.xml声明的基础上增加运行时的权限申请才能进行相关API的调用,这对终端用户的隐私保护是一大利好,浏览一下国内的很多应用,一个很简单的APP也会申请Contact、Location、ID、SMS、 *History等权限。自从6.0之后App安装完成未启动前你就可以去应用设置中关闭没必要的权限。当然国内的很多定制ROM比如MIUI、EMUI等在之前android版本中已经定制了相关模型,但这一次是从API级别上标准化了这一设置,所有支持后续版本的APP都必须实现这一原则。

相关运行时权限申请开发过程,可通过Android官方网站浏览。更多Android权限相关

这里只说明了几个开发要点:

当前流行的运行时权限申请流程有两种:

  • 在App启动时强制检测所有预申请权限,有一项必须权限用户没有授权则弹出对话框提示到应用设置页面进行授权打开。
  • 在App运行时需要某一项或者多项权限时临时授权,然后继续下一项操作

当前国内应用大多采用a)方法,微信、支付宝采用此方法,可能导致后面一票儿的App采用这种方式,而且有些非必要权限一旦拒绝授权,应用立刻退出,体验相当暴力。Google系、国外其他App大多采用b)方法,非必要情况下不会预先申请授权。

采用a)简单直接但是用户体验相当冲突,尤其是用户拒绝申请授权时,所以建议使用方法b)。

多必要权限的同时申请

由于调用ContextCompat.checkSelfPermission()时只能进行一项权限检查导致后续的requestPermissions()虽然方法支持多项权限的申请,但在此情况下只能进行一项权限授权申请,进而导致后续只能在onRequestPermissionsResult()检查第一项权限授权是否通过后再进行下一项授权申请。这其间onResume()调用还需要记录每一项权限的授权结果。

方法:构建一个权限数组,onStart()时每次循环遍历所有预申请权限,如果所有权限均授权通过,则进行下一步操作,如果是未授权,则调用requestPermissions(),如果是拒绝,则提示用户跳转APP设置界面进行授权。在onRequestPermissionsResult()方法中不检查权限的授权结果而是继续调用循环遍历来检查权限授权结果。这里需要注意一个无限循环的问题,如下。

权限授权过程无限循环

requestPermissions()调用后弹出的是由PackageManager管理的Dialog主题的Activity(B),如果此时用户屏幕锁屏,B调用onStop(),授权申请Activity(A)调用onStop(),开屏后A将触发onStart()再次调用requestPermissions(),此时B还在前台,再创建B‘实例导致B回调onRequestPermissionsResult()继续重复要点2的逻辑,导致进入无限循环,在API>23后会有多次请求权限授权的日志输出,可以注意观察。为了避免上述情况发生,可以在授权申请Activity(A)添加标记位避免开锁屏时的重复调用或者锁屏后的onStop()中发起取消申请授权,然后在onRequestPermissionsResult()中判断是否要继续调用。

取消授权申请

在公开的API中并没有在申请授权过程中的取消操作,因为运行时授权申请本身并不允许并行执行,所以当前状态下只有三种可能性:不再提醒、允许、拒绝。但当出现上述3逻辑时就需要有可以取消的流程。在Activity.java的源码中提供了一种方式:  // Dispatch the callback with empty arrays which means a cancellation.
 onRequestPermissionsResult(requestCode, new String[0], new int[0]);

通过主动调用授权结果返回,然后根据后面两个参数来结束授权流程。

Fragment、Activity中的使用

Fragment中直接使用父类的requestPermissions()

Activity兼容模式下调用ActivityCompat.requestPermissions()

友好性的授权拒绝提示

微信、支付宝都是采用对话框的方式,但在用户体验上很冲突,推荐采用SnackBar