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

作者:马果果 时间:2021-09-16 07:56:34 

前言

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

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

从数据来看确实没那么高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以前我们只需要在清单文件中声明该权限即可。

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

关于Android 6.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() {
 //在这表示用户同意了权限申请。
 //假如用户拒绝了权限申请在这儿我是没让他进入到应用中的效果如下
}
})

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

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

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

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

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

还有个剧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:

  • 发照片时,申请照片权限

  • 发语音时,申请麦克风权限

  • 用户每次点击拒绝,都弹出自定义对话框,提示用户设置权限

来源:https://juejin.im/post/5a0444fb6fb9a0450c48dba2

标签:android6.0,权限,动态适配
0
投稿

猜你喜欢

  • C#实现动态执行字符串脚本(优化版)的示例代码

    2022-06-10 19:13:54
  • C#实现文件上传以及多文件上传功能

    2023-06-19 15:36:17
  • Android实现自动提取短信验证码功能

    2022-11-19 23:58:49
  • 详解Java设计模式——命令模式

    2023-11-23 05:00:07
  • 如何实现Spring Event(异步事件)

    2023-08-23 05:06:47
  • struts2简介_动力节点Java学院整理

    2023-06-24 02:04:07
  • Android 配置gradle实现VersionCode自增实例

    2021-07-03 12:39:23
  • 详解Java实现数据结构之并查集

    2023-09-05 08:47:06
  • java实现简易的学籍管理系统

    2023-07-21 07:47:26
  • C#微信开发第一章

    2022-02-14 19:16:31
  • Java 中的 String对象为什么是不可变的

    2023-08-04 03:24:32
  • SpringMVC实现表单验证功能详解

    2023-09-24 09:07:28
  • 如何在IDEA Maven项目中导入本地jar包的步骤

    2023-03-25 06:43:48
  • Spring Boot 整合JPA 数据模型关联使用操作(一对一、一对多、多对多)

    2022-11-18 23:14:29
  • mybatis-plus分页查询三种方法小结

    2023-11-13 08:44:26
  • C#实现支付宝沙箱支付的项目实践

    2021-07-29 17:18:18
  • 关于C#连接SQL Server时提示用户登录失败的解决方法

    2021-10-03 21:21:58
  • Java实现的简单网页截屏功能示例

    2021-05-25 13:50:35
  • java中使用zxing批量生成二维码立牌

    2021-12-31 04:31:03
  • C# 操作XML文档 使用XmlDocument类方法

    2023-06-11 04:21:14
  • asp之家 软件编程 m.aspxhome.com