关于Android 6.0权限的动态适配详解

前言

Android6.0代号棉花糖。尽管是在15年I/O大会上Google被正式发布的了。但是看看大多数人的项目中大家的 targetSdkVersion 是不是还都用的22。大家都认为6.0+的市场占有率还没那么高。那么就请看谷歌2017年9月份公布的版本分布图。

从数据来看确实没那么高O(∩_∩)O。6.0+的市场占有率仅为50% ̄□ ̄||。只因安卓用户的基数太大了吧。延伸至各种人群。虽然说占比才一半但时基数大总的用户数量还是蛮多的。这两天刚做完6.0权限的适配。那么请说一下自己测试的时候踩的坑吧(*╹▽╹*)

权限管理系统的变化

在Android6.0(M)之前,在用户安装应用的时候会产生一个权限列表,只有用户允许这些权限后,应用才可以正常的安装,这就会产生一个问题,这些权限对用户是不具有感知性的,也就是说用户都不知道你要这些权限干什么,我明明装的是一个阅读类型的应用,你却要我拨打电话的权限,你想干嘛呢?当然绝大部分的开发者是善意的,但也避免不了一些特殊人群利用这些“漏洞”做一些不好的事情。

而在Android6.0(M)之后,用户是可以不管权限直接安装应用的,当应用需要调用某些权限的时候,会给予用户一个通知与说明,我要这些权限干什么,这样下来可以让用户有更加清醒的权限分配意识,也在一定程度上更加人性化的保护了用户的隐私,避免了“权限一刀切”。

权限的分组

在Android6.0(M)之后,对权限进行了分类,大致有这三种:

  • 普通权限
  • 危险权限
  • 特殊权限

普通权限:也就是正常权限,是对手机的一些正常操作,对用户的隐私没有太大影响的权限,比如手机的震动,网络访问,蓝牙等权限,这些权限会在应用被安装的时候默认授予,用户不能拒绝,也不能取消。

普通权限列表:

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
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
SET_ALARM
INSTALL_SHORTCUT
UNINSTALL_SHORTCUT

对于上面这些普通权限 在Android6.0以前我们只需要在清单文件中声明该权限即可。

危险权限:其实就是运行中需要处理的权限,也是我们最需要注意的权限,这些权限会关系到用户的隐私或影响到其他应用的运行,这些危险权限,谷歌还做了一个权限组,以分组的形式来呈现:

由于运行权限机制的出现,我们需要对新开发的应用去做适配。

当你的应用targetSdkVersion小于23的时候,当应用用于6.0以上的系统时候,它也会默认采用以前的权限管理机制。当你的targetSdkVersion大于等于23的时候且在Andorid6.0(M)系统上,它才会采用新的这套权限管理机制。

所以如果你想逃开这个“麻烦”,只要把targetSdkVersion的版本设置为低于23就可以了,不过不建议采用这种方案,该来的总是要来的,随着国产手机ROM的更新,比如小米,华为等也开始有部分机型进行了系统升级,所以这是种趋势。

说了这么多,那么来看下怎么进行Android6.0(M)的权限管理适配吧,其实很简单,只需要记住下面几个API方法就可以:(API23之后提供)

int checkSelfPermission(String permission) 用来检测应用是否已经具有权限void requestPermissions(String[] permissions, int requestCode) 进行请求单个或多个权限void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 请求权限结果回调

checkSelfPermission(String permission) 方法返回值有两个:

  • PERMISSION_DENIED = -1:代表当前检查的权限没有被授权
  • PERMISSION_GRANTED = 0;代表当前的检查的权限已经被授权

requestPermissions(String[] permissions, int requestCode)

参数一:要请求的权限组   权限2请求码

onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)请求的回调。

参数3对应 对应permissions的权限请求结果(PERMISSION_GRANTED或者PERMISSION_DENIED)

看完关键的三个方法接下来上我的油条:

object MQPermissionUtil {
 private var mRequestCode = -1
 private val isOverMarshmallow: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
 private var mOnPermissionListener: OnPermissionListener? = null

 fun requestPermissions(activity: Activity, requestCode: Int, permissions: Array<String>, isCancelFinish: Boolean, onPermissionGrantedListener: OnPermissionGrantedListener) {
  requestPermissionsResult(activity, requestCode, permissions, object : OnPermissionListener {
   override fun onPermissionGranted() {
    onPermissionGrantedListener.onPermissionGranted()
   }

   override fun onPermissionDenied() {
    if (isCancelFinish) {
     showTipsDialogWel(activity)
    } else {
     showTipsDialog(activity)
    }
   }
  })
 }

 private fun requestPermissionsResult(activity: Activity, requestCode: Int, permissions: Array<String>, callback: OnPermissionListener) {
  mOnPermissionListener = callback
  if (checkPermissions(activity, *permissions)) {
   if (mOnPermissionListener != null)
    mOnPermissionListener!!.onPermissionGranted()
  } else {
   val deniedPermissions = getDeniedPermissions(activity, *permissions)
   if (deniedPermissions.isNotEmpty()) {
    mRequestCode = requestCode
    ActivityCompat.requestPermissions(activity, deniedPermissions
      .toTypedArray(), requestCode)
   }
  }
 }

 fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
  if (requestCode == mRequestCode) {
   if (verifyPermissions(grantResults)) {
    if (mOnPermissionListener != null)
     mOnPermissionListener!!.onPermissionGranted()
   } else {
    if (mOnPermissionListener != null)
     mOnPermissionListener!!.onPermissionDenied()
   }
  }
 }

 private fun startAppSettings(context: Context) {
  val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
  intent.data = Uri.parse("package:" + context.packageName)
  context.startActivity(intent)
 }

 private fun verifyPermissions(grantResults: IntArray): Boolean {
  if (grantResults.isEmpty())
   return false

  // 循环判断每个权限是否被拒绝
  for (grantResult in grantResults) {
   if (grantResult != PackageManager.PERMISSION_GRANTED) {
    return false
   }
  }
  return true
 }

 private fun getDeniedPermissions(context: Context, vararg permissions: String): List<String> {
  val deniedPermissions = ArrayList<String>()
  for (permission in permissions) {
   if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) {
    deniedPermissions.add(permission)
   }
  }
  return deniedPermissions
 }

 private fun checkPermissions(context: Context, vararg permissions: String): Boolean {
  if (isOverMarshmallow) {
   for (permission in permissions) {
    if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) {
     return false
    }
   }
  }
  return true
 }

 fun showTipsDialog(activity: Activity) {
  AlertDialog.Builder(activity)
    .setTitle("提示信息")
    .setMessage("当前应用缺少必要权限,無法正常使用,请单击【确定】按钮前往设置中心进行权限授权。")
    .setNegativeButton("取消", null)
    .setPositiveButton("确定") { _, _ ->
     activity.finish()
     startAppSettings(activity)
    }.show()
 }

 fun showTipsDialogWel(activity: Activity) {
  AlertDialog.Builder(activity)
    .setTitle("提示信息")
    .setMessage("当前应用缺少必要权限,無法正常使用,请单击【确定】按钮前往设置中心进行权限授权。")
    .setNegativeButton("取消") { _, _ -> activity.finish() }
    .setPositiveButton("确定") { _, _ ->
     activity.finish()
     startAppSettings(activity)
    }.show()
 }

 interface OnPermissionGrantedListener {
  fun onPermissionGranted()
 }

 interface OnPermissionListener {
  fun onPermissionGranted()
  fun onPermissionDenied()
 }
}

写的不好。大家自行修改吧。

Activity中的使用在onCreate中一开始调用一下代码:

MangoPermissionUtil.requestPermissions(this@IndexActivity, Constant.PERMISSION_OPERATION_CODE_SCAN, arrayOf(Manifest.permission.CAMERA), false, object : MangoPermissionUtil.OnPermissionGrantedListener {
 override fun onPermissionGranted() {
  //在这表示用户同意了权限申请。
  //假如用户拒绝了权限申请在这儿我是没让他进入到应用中的效果如下
 }
})

只要有任何一个权限用户没通过都会弹出这个Dialog。直到用户全部授权。。。。

点击取消退出应用。确定按钮去到设置界面为应用授权。。。。

下面是应用启动的场景(很舒服2333)

还有个剧TM恶心的问题这些所有的逻辑在除了小米6.xxx的设备上跑是没问题的。必须全部授权才能进入应用。但是小米6.xxx的设备上当我第一次拒绝了权限申请之后。第二次进入应用判断权限的时候它竟然在checkPermisssion的方法中给我返回了PERMISSION_GRANTED这就比较尴尬了。这样我是可以进入掉权限请求成功的回调。但是我进去之后确实没权限啊。对应权限相关的操作一样不能执行。。。不得不说小米的6.xxx设备是真的坑。。。。

还有一点油条用的时候还要在当前申请的Activity中调用一下来执行到油条中自定义的回调

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
 MangoPermissionUtil.onRequestPermissionsResult(requestCode, permissions, grantResults)
 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
}

最后

对于一些比较特别的权限,比如文件的读写权限,一般在我们第一次开启APP的时候就要去获取了,假设我们一开始没有获取到这个权限,那么如果我的首页有轮播广告图,这个广告图是网络获取的,做了三级缓存,这样就会到导致磁盘缓存无法写入。这边提供一个解决方法,就是在你引导APP启动的时候,就引导用户去获取权限,当用户拒绝的时候,应该给出弹出框并跳转对应的应用权限管理界面(需要对不同机型进行设置)。

可以参考微信的做法:

启动app,在闪屏页的时候向用户提出权限的申请

  • 存储空间权限,关闭微信
  • 电话权限,关闭微信
  • 位置权限,关闭微信
  • 进入app:
  • 发照片时,申请照片权限
  • 发语音时,申请麦克风权限
  • 用户每次点击拒绝,都弹出自定义对话框,提示用户设置权限

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Android6.0仿微信权限设置

    Android 6.0版本对于程序员兄弟来说最不友好的就是权限的问题,动态权限的设置曾经让我很苦恼,目前大部分关于6.0权限设置的框架基本都是一次性访问多个权限(EasyPermissions),这样导致的问题就是如果我们申请了三种权限,而用户只同意了其中一种,下次再申请权限又是一次性申请三种,很不方便对于用户来说很不友好,偶然情况下发现了安卓猴的这篇文章, http://sunjiajia.com/2016/04/19/android-m-permissions/ 在此基础上做了修改,就实现了

  • 详解Android6.0运行时权限管理

    自从Android6.0发布以来,在权限上做出了很大的变动,不再是之前的只要在manifest设置就可以任意获取权限,而是更加的注重用户的隐私和体验,不会再强迫用户因拒绝不该拥有的权限而导致的无法安装的事情,也不会再不征求用户授权的情况下,就可以任意的访问用户隐私,而且即使在授权之后也可以及时的更改权限.这就是6.0版本做出的更拥护和注重用户的一大体现. 一.认知 今天我们就来学习下Android6.0的权限管理. Android6.0系统把权限分为两个级别: 一个是Normal Permiss

  • Android6.0动态申请权限所遇到的问题小结

    白天在做SDK23版本的适配,遇到了不少坑,现在抽空记下来,以此为戒. 首先要知道哪些坑,就得先了解一些定义和基本使用方式. 那么先介绍一下动态申请的权限分组情况. 下面的权限组是由谷歌官方定义的,目的是在申请权限时,只要用户允许同一权限组的任意一条权限,那么该组的其他权限也就默认是允许的.不过据高人介绍,在使用时最好是用到哪个权限就具体的请求该权限,因为保不齐哪天谷歌一高兴就把权限组换了甚至删了 group:android.permission-group.CONTACTS permissio

  • 详解Android权限管理之Android 6.0运行时权限及解决办法

    前言: 今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以上设备越来越多了,所以Android 6.0 权限适配是必不可少的工作,这里主要介绍一下我们公司是如何做Android 6.0权限适配的. Android 6.0以下非运行时权限: 根据上面博客我们很清楚的知道,Android的权限其实就是为了程序之间更加的安全的访问,所以权限有等级之分,比如:No

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

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

  • 谈谈Android6.0运行时的权限处理

    运行时权限介绍 Android 6.0在我们原有的AndroidManifest.xml声明权限的基础上, 又新增了运行时权限动态检测,以下权限都需要在运行时判断: 1.身体传感器 2.日历    3.摄像头 4.通讯录 5.地理位置 6.麦克风 7.电话 8.短信 9.存储空间 在 Android 6.0 中,app 如果想要获得某些权限,会在应用中弹出一个对话框,让用户确认是否授予该权限. 具体的截图如下: 这要做的好处就是运行一个 app 时可以拒绝其中的某些权限,防止 app 触及到你的

  • Android 6.0权限申请详解及权限资料整理

    在android 6.0开始,部分的权限需要我们动态申请,也就是说当我们的打开app的时候系统不会主动像您申请app所需要的部分权限,需要客户在使用app的时候主动的去申请. 一.权限的申请两步骤: 1.权限申请: /** * @param permissions需要申请的权限 * @param requestCode申请回调code */ public static void requestPermissions(final @NonNull Activity activity,final @

  • 详解Android权限管理之RxPermission解决Android 6.0 适配问题

    前言: 上篇重点学习了Android 6.0的运行时权限,今天还是围绕着Android 6.0权限适配来总结学习,这里主要介绍一下我们公司解决Android 6.0权限适配的方案:RxJava+RxPermission.这里不再介绍Android 6.0运行时权限了,直接看下如何使用RxPermission. RxPermission: 用于适配Android 6.0新的权限模型的开源框架. 下载地址:点此下载 如何使用? 1.)在app module的build.gradle中添加如下配置 使

  • Android获取设备隐私 忽略6.0权限管理

    一.前言 (1).由于MIUI等部分国产定制系统也有权限管理,没有相关api,故无法判断用户是否允许获取联系人等隐私.在Android 6.0之后,新增权限管理可以通过官方api判断用户的运行状态: (2).我们指定targetSdkVersion为23或者之后我们还需要在运行时请求这些所需的权限.这很重要,因为已经出现了很多开发者把targetSdkVersion飙到了最新,然后发现自己的app疯狂的崩溃,这是由于他们没有实现执行运行时权限请求的代码.当你已经把一个targeting API

  • Android 6.0动态权限申请教程

    PermissionManage 项目地址:https://github.com/why168/AndroidProjects/tree/master/PermissionManage 介绍 如果设备运行的是 Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本,则应用在运行时向用户请求权限. 如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更

随机推荐