PermissionX 1.7发布,全面支持Android 13运行时权限

2023-10-28

c049cfdd25e409fceda159f13cf6ab72.jpeg

各位小伙伴们大家早上好。

一年一度的PermissionX升级又来了。

还记得上次发布PermissionX 1.6版本还是在去年10月份的时候,当时是对Android 12系统进行了支持。详情可以参考这篇文章 PermissionX 1.6发布,支持Android 12,可能是今年最大的版本升级 。

而如今一年一晃而过,Android 13也已经正式发布了。

今年的Android 13在运行时权限变更方面变化较大,为此PermissionX在1.7版本也进行了诸多适配,并已全面支持Android 13系统。

如果你想要非常详细地了解Android 13运行时权限具体有哪些变更,可以参考我之前写的这篇文章 Android 13运行时权限变更一览

本篇文章,我们将聚焦在,如何使用新版的PermissionX来轻松地请求Android 13上的新增运行时权限。

那么Android 13上一共有哪些新增运行时权限呢?

我这里做了下统计,一共新增了6个运行时权限,如下所示:

READ_MEDIA_IMAGES
READ_MEDIA_VIDEO
READ_MEDIA_AUDIO
POST_NOTIFICATIONS
BODY_SENSORS_BACKGROUND
NEARBY_WIFI_DEVICES

这种幅度的改动已经算是非常大了。要知道,Android 12只新增了4个运行时权限,Android 11甚至没有新增任何运行时权限。

其实如果仅从简单的方面来讲,一个权限请求框架并不需要对每个版本新增的运行时权限做什么适配,因为运行时权限请求的方式都是同样的。

但PermissionX不是一个简单的权限请求框架,而是设计了一套完整的权限请求流程。包括权限被用户拒绝时要如何提醒用户,被永久拒绝时要如何引导用户手动开启权限,以及一些特殊权限的特殊处理。

这些都导致了PermissionX必须对每个系统版本进行额外的适配才行,但好处就是,开发者在使用的过程当中将会变得更加轻松,从而不需要自己再去做这些适配工作了。

那么接下来我们就来看一下,PermissionX是如何对上述6个新增运行时权限进行适配的。

/   细化的媒体权限   /

从Android 13开始,如果你的应用targetSdk指定到了33或以上,那么READ_EXTERNAL_STORAGE权限就完全失去了作用,申请它将不会产生任何的效果。

与此相对应地,Google新增了READ_MEDIA_IMAGES、READ_MEDIA_VIDEO和READ_MEDIA_AUDIO这3个运行时权限,分别用于管理手机的照片、视频和音频文件。

也就是说,以前只要申请一个READ_EXTERNAL_STORAGE权限就可以了。现在不行了,得按需申请,用户从而能够更加精细地了解你的应用到底申请了哪些媒体权限。

而使用PermissionX申请这3个权限非常简单。首先在AndroidManifest.xml文件中进行如下声明:

<manifest ...>
    <!-- Required only if your app targets Android 13. -->
    <!-- Declare one or more the following permissions only if your app needs
    to access data that's protected by them. -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

    <!-- Required to maintain app compatibility. -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
                     android:maxSdkVersion="32" />
    <application ...>
        ...
    </application>
</manifest>

这里的意思是,在Android 12及以下,仍然使用READ_EXTERNAL_STORAGE权限,在Android 13及以上则使用READ_MEDIA_IMAGES、READ_MEDIA_AUDIO和READ_MEDIA_VIDEO权限。

然后在代码中使用如下写法来申请这3个权限即可:

val requestList = ArrayList<String>()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestList.add(Manifest.permission.READ_MEDIA_IMAGES)
    requestList.add(Manifest.permission.READ_MEDIA_AUDIO)
    requestList.add(Manifest.permission.READ_MEDIA_VIDEO)
}
if (requestList.isNotEmpty()) {
    PermissionX.init(activity)
        .permissions(requestList)
        .onExplainRequestReason { scope, deniedList ->
            val message = "PermissionX需要您同意以下权限才能正常使用"
            scope.showRequestReasonDialog(deniedList, message, "Allow", "Deny")
        }
        .request { allGranted, grantedList, deniedList ->
            if (allGranted) {
                Toast.makeText(activity, "所有申请的权限都已通过", Toast.LENGTH_SHORT).show()
            } else {
                Toast.makeText(activity, "您拒绝了如下权限:$deniedList", Toast.LENGTH_SHORT).show()
            }
        }
}

PermissionX允许一次性传入任意多个权限,然后批量向用户进行申请。这里我们将3个权限全部都传进去了。

需要说明的是,READ_MEDIA_IMAGES和READ_MEDIA_VIDEO这两个权限同属于一个权限组,READ_MEDIA_AUDIO属于另外一个权限组。

Android系统规定,同一个权限组中的权限,只要授予了其中一个,同组的其他权限也就都自动授予了。

因此,虽然这里我们申请了3个权限,但是只会看到两次请求弹窗,如下图所示:

0fda1b864ef0b0624fd77c7d7c86f5ba.gif

可以看到,在两次权限请求弹窗当中,我们同意了一个,拒绝了另外一个。PermissionX此时会自动弹出一个解释弹窗,告知用户为什么这个权限是必要的,并引导用户再次授权。

其实想要适配好这种逻辑是需要写很多额外代码的,而PermissionX帮我们都自动封装了,权限请求就变得简单多了。

/   通知权限   /

通知权限可以说是Android 13的重磅功能之一。

在过去,任何一个应用想要发出通知的话都是不需要经过用户同意的,想发就能发。这就使得我们的手机通知栏经常被一些垃圾通知占领,真正重要的通知反而可能很难被找到。

这次Android 13则把通知纳入了运行时权限管理,也就是说,以后想要发送通知,得要先经过用户同意授权才行了。

对于用户而言,这确实是非常友善的功能,以后就不用担心再被各种垃圾通知所骚扰了。但是对于开发者而言,这个功能反而使得我们的适配工作变得各加困难。

因为在Android 13以下的系统,虽然应用程序可以不经过用户同意就发送通知,但是用户也有权力去屏蔽任何应用程序的通知,效果等同于用户拒绝了通知权限。

那么为了防止用户屏蔽掉了一些重要通知,某些应用的做法是主动检测通知是否被屏蔽,如果屏蔽的话就引导用户去设置页面手动开启。

而Android 13引入通知权限之后,以前的写法就不行了。所以开发者需要为Android 13以上和以下的系统分别进行适配才可以,无形中增加了开发的工作量。

因此,PermissionX 1.7版本在设计的时候就充分考虑到了这个问题,目标就是使得开发者的接入工作尽可能地最简单化。

下面我们就来看看使用PermissionX具体要如何申请通知权限,首先在AndroidManifest.xml文件中进行如下声明:

<manifest ...>
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
    <application ...>
        ...
    </application>
</manifest>

然后在代码中使用如下写法来申请POST_NOTIFICATIONS权限即可:

PermissionX.init(activity)
    .permissions(PermissionX.permission.POST_NOTIFICATIONS)
    .onExplainRequestReason { scope, deniedList ->
        val message = "PermissionX需要您同意以下权限才能正常使用"
        scope.showRequestReasonDialog(deniedList, message, "Allow", "Deny")
    }
    .request { allGranted, grantedList, deniedList ->
        if (allGranted) {
            Toast.makeText(activity, "所有申请的权限都已通过", Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(activity, "您拒绝了如下权限:$deniedList", Toast.LENGTH_SHORT).show()
        }
    }

没错,代码就是这么简单。

注意这里一个非常关键的细节,我们并没有使用Manifest.permission.POST_NOTIFICATIONS,而是使用了PermissionX.permission.POST_NOTIFICATIONS。

这是因为POST_NOTIFICATIONS是Android 13的新增权限,以前的系统版本是没有的,因此如果使用Manifest.permission.POST_NOTIFICATIONS在Android 13以下系统可能会导致编译不过。

而刚才我又说了,PermissionX为了简化开发者的适配工作,将Android 13以下的通知权限也一并处理了。因此,上述代码在所有的Android版本上都可以正常工作。

那么我们先来看一下上述代码在Android 13上的运行效果,如下图所示:

e33c08efaef8ad5770dc8171916c5f91.gif

可以看到,这里会直接向用户申请通知权限,如果用户选择了拒绝,那么会再弹出一个PermissionX的提示框,告诉用户为什么我们需要这个权限。

然后再来看一下上述代码在Android 12上的运行效果。

需要注意还有一个细节,每个应用程序的通知开关在Android 12及以下系统都是默认开启的。这也是前面为什么说任何应用想要发送通知是不需要经过用户同意的,想发就能发。

因此为了验证上述代码是否能正常工作,我们还得先手动把当前应用的通知开关给关掉才行,如下图所示:

6e96b7e8fb3699586faed19d65056031.png

接下来验证效果如下:

916fa8e694a3247779d8ea907d2d7780.gif

可以看到,这里首先会弹出一个PermissionX的提示框,提醒用户需要手动打开设置当中的通知开关才行。

只要用户点击了同意,那么就会一键跳转到当前应用的通知设置界面,让用户用最低的操作成本来打开通知开关。

而这一系列功能只需要写上述一份代码就可以实现了,这就是PermissionX给开发者所带来的便利性。

/   后台运动传感器权限   /

还有一个变化是运动传感器权限。

之前我们如果想要读取手机运动传感器的数据,需要申请BODY_SENSORS权限。而在Android 13当中,Google给BODY_SENSORS权限又添加了一个只能在前台使用的限定。

72073bdfad8f1101b6dec99355de074a.png

可以看到,在Android 13上申请BODY_SENSORS权限时,用户只能授权在前台使用。

那么如果我们的应用程序就是要在后台获取运动传感器数据怎么办呢?这个时候就需要用到Android 13上另一个新增的运行时权限,BODY_SENSORS_BACKGROUND。

但是根据我的测试,申请BODY_SENSORS_BACKGROUND这个权限的坑非常多。

首先,申请BODY_SENSORS_BACKGROUND权限之前必须得要先获得BODY_SENSORS授权才行,不然申请就是无效的,会被直接拒绝。这个设定有点像当初Android 10增加后台获取地理位置权限的设定。

其次,BODY_SENSORS和BODY_SENSORS_BACKGROUND权限还不可以同时一起申请,不然的话两个权限会一同被拒绝。这种奇怪的设定我也是第一次见。

所以正确的做法是,要先申请BODY_SENSORS权限,在这个权限获得授权之后,再去申请BODY_SENSORS_BACKGROUND才行。

那么,你的脑海中是否已经勾勒出代码应该怎么写了呢?

不用费脑子了,因为PermissionX已经帮我们把这些适配逻辑都处理好了。

使用PermissionX来申请运动传感器权限,只需要把BODY_SENSORS和BODY_SENSORS_BACKGROUND一同传给PermissionX即可。

我们还是通过代码来验证一下,首先在AndroidManifest.xml中对这两个权限进行声明:

<manifest ...>
    <uses-permission android:name="android.permission.BODY_SENSORS"/>
    <uses-permission android:name="android.permission.BODY_SENSORS_BACKGROUND"/>
    <application ...>
        ...
    </application>
</manifest>

接下来使用如下代码来申请运动传感器权限即可:

val requestList = ArrayList<String>()
requestList.add(Manifest.permission.BODY_SENSORS)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestList.add(Manifest.permission.BODY_SENSORS_BACKGROUND)
}
PermissionX.init(activity)
    .permissions(requestList)
    .onExplainRequestReason { scope, deniedList ->
        val message = "PermissionX需要您同意以下权限才能正常使用"
        scope.showRequestReasonDialog(deniedList, message, "Allow", "Deny")
    }
    .request { allGranted, grantedList, deniedList ->
        if (allGranted) {
            Toast.makeText(activity, "所有申请的权限都已通过", Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(activity, "您拒绝了如下权限:$deniedList", Toast.LENGTH_SHORT).show()
        }
    }

PermissionX在内部做的事情其实就是我们上述分析的内容。先申请BODY_SENSORS权限,在这个权限获得授权之后,再去申请BODY_SENSORS_BACKGROUND。但是由于框架内部做好了封装,我们再去编写申请权限的代码就可以变得非常简洁了。

来看一下运行效果吧,如下图所示:

5fd0a96dd33699ff30f3cb5c07fa97a3.gif

可以看到,这里一开始只会申请前台的运动传感器权限。当用户选择了While using the app之后,PermissionX会弹出一个提醒框,告诉用户还需要同意后台运动传感器权限才行。之后会直接带用户来到当前应用程序的运动传感器权限设置界面,点击Allow all the time即可完成授权。

整个流程其实非常繁琐,但是Google就是这样设计的。可能也是故意搞得繁琐一点,让用户更加注重保护自己的隐私,而不是简单一键就把所有权限统统都交给这些应用程序了。

而PermissionX则在Android 13规则允许的前提下,将权限申请流程以及申请代码书写都尽可能做到了最简化。让普通用户和开发者都能享受到更好的用户体验。

/   附近WiFi设备权限   /

去年,Google在Android 12当中新增了几个蓝牙相关的运行时权限。原因是因为当开发者去访问一些蓝牙相关的接口时,却需要申请地理位置权限才行,这就让一些对隐私敏感的用户非常反感。

这是一个历史遗留问题,为了更好地保护用户隐私,Google在Android 12当中增加了BLUETOOTH_SCAN,BLUETOOTH_ADVERTISE,BLUETOOTH_CONNECT,这3个运行时权限。这样当开发者需要访问蓝牙相关的接口时,只需要请求这些蓝牙权限即可。

而在今年的Android 13当中,Google将保护用户隐私延伸到了WIFI领域。

和蓝牙类似,当开发者去访问一些WIFI相关的接口时,如热点、WIFI直连、WIFI RTT等,也需要申请地理位置权限才行。

这其实也是一个历史遗留问题,用户肯定无法理解为什么使用一些WIFI功能时却需要授权地理位置权限。

为此,Android 13当中新增了一个NEARBY_WIFI_DEVICES权限,当再使用以上场景相关的WIFI API时,我们只需申请NEARBY_WIFI_DEVICES权限即可,从而更好地保护了用户的隐私。

这是一个非常普通的运行时权限,PermissionX并没有对它进行任何适配,也没有什么坑点,需要申请它的时候直接去申请即可。

AndroidManifest.xml文件中的声明如下:

<manifest ...>
    <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"/>
    <application ...>
        ...
    </application>
</manifest>

权限申请代码示例如下:

PermissionX.init(activity)
    .permissions(Manifest.permission.NEARBY_WIFI_DEVICES)
    .onExplainRequestReason { scope, deniedList ->
        val message = "PermissionX需要您同意以下权限才能正常使用"
        scope.showRequestReasonDialog(deniedList, message, "Allow", "Deny")
    }
    .request { allGranted, grantedList, deniedList ->
        if (allGranted) {
            Toast.makeText(activity, "所有申请的权限都已通过", Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(activity, "您拒绝了如下权限:$deniedList", Toast.LENGTH_SHORT).show()
        }
    }

运行效果图如下:

7e661e98302b8dc2840d50900f57c81a.gif

都是非常标准和基础的写法,我就不做更多解释了。

/   如何升级   /

关于PermissionX新版本的内容变化就介绍到这里,升级的方式非常简单,修改一下dependencies当中的版本号即可:

repositories {
  google()
  mavenCentral()
}

dependencies {
    implementation 'com.guolindev.permissionx:permissionx:1.7.1'
}

如果你对PermissionX的源码感兴趣,可以访问PermissionX的项目主页:

https://github.com/guolindev/PermissionX

另外,本篇文章主要介绍的是PermissionX 1.6.0版本的新特性。如果你之前并没有接触过PermissionX,可以到我的公众号主页->历史文章->权限系列,查看我写的关于PermissionX的所有文章,里面有非常详尽的用法讲解。

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

Android 13 Developer Preview一览

模仿Android微信小程序,实现小程序独立任务视图的效果

欢迎关注我的公众号

学习技术或投稿

6979d1ff60790e6a8555023068632a3c.png

9ac33db3f5b3e217e868dedbf1151dee.jpeg

长按上图,识别图中二维码即可关注

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PermissionX 1.7发布,全面支持Android 13运行时权限 的相关文章

  • Karaf / Maven - 无法解决:缺少需求 osgi.wiring.package

    我无法在 Karaf 版本 3 0 1 中启动捆绑包 该包是使用 Maven 构建的并导入gson http mvnrepository com artifact com google code gson gson 2 3 1 我按照要求将
  • IntelliJ 组织导入

    IntelliJ 是否具有类似于 Eclipse 中的组织导入功能 我拥有的是一个 Java 文件 其中多个类缺少导入 例子 package com test public class Foo public Map map public J
  • 在骨架图像中查找线 OpenCV python

    我有以下图片 我想找到一些线来进行一些计算 平均长度等 我尝试使用HoughLinesP 但它找不到线 我能怎么做 这是我的代码 sk skeleton mask rows cols sk shape imgOut np zeros row
  • 使用Java绘制维恩图

    我正在尝试根据给定的布尔方程绘制维恩图 例如 a AND b AND c我想在 Android 手机上执行此操作 因此我需要找到一种使用 Java 来执行此操作的方法 我找到了一个完美的小部件 它可以完成我在这方面寻找的一切布尔代数计算器
  • 在Raspberry pi上升级skimage版本

    我已经使用 Raspberry Pi 2 上的 synaptic 包管理器安装了 python 包 然而 skimage 模块版本 0 6 是 synaptic 中最新的可用版本 有人可以指导我如何将其升级到0 11 因为旧版本中缺少某些功
  • XPath:通过当前节点属性选择当前和下一个节点的文本

    首先 这是从我之前的问题 https stackoverflow com questions 5202187 xpath select current and next nodes text by current node attribut
  • 可以使用哪些技术来衡量 pandas/numpy 解决方案的性能

    Question 如何简洁全面地衡量下面各个功能的性能 Example 考虑数据框df df pd DataFrame Group list QLCKPXNLNTIXAWYMWACA Value 29 52 71 51 45 76 68 6
  • 当目标小于 Android O 时,如何在 Android O 上创建快捷方式?

    背景 Android O 对快捷方式的工作方式进行了各种更改 https developer android com preview behavior changes html as https developer android com
  • 无需登录即可直接从 Alfresco 访问文件/内容

    我的场景是这样的 我有一个使用 ALFRESCO CMS 来显示文件或图像的 Web 应用程序 我正在做的是在 Java servlet 中使用用户名和密码登录 alfresco 并且我可以获得该登录的票证 但我无法使用该票证直接从浏览器访
  • 如何让 Emma 或 Cobertura 与 Maven 一起报告其他模块中源代码的覆盖率?

    我有一个带有 Java 代码的多模块 Maven 设置 我的单元测试在其中一个模块中测试多个模块中的代码 当然 这些模块具有相互依赖性 并且在测试执行之前根据需要编译所有相关模块中的代码 那么 如何获得整个代码库覆盖率的报告 注意 我不是问
  • 检测是否从psycopg2游标获取?

    假设我执行以下命令 insert into hello username values me 我跑起来就像 cursor fetchall 我收到以下错误 psycopg2 ProgrammingError no results to fe
  • 离子初始加载时间

    我正在使用 Ionic 构建一个简单的应用程序 但我的应用程序在冷启动时的初始加载时间方面存在性能问题 这是我所做的 collection repeat 代替带有 track by 的 ng repeat 原生滚动 overflow scr
  • Android 标记如何实现拖放?

    你好 我正在 Android 中开发 MapView 应用程序 我有三个标记 我希望稍后能够使用 Google Map API getlocation function 为了尝试一下 我想使用拖放功能移动标记 然后检查位置 任何人都可以通过
  • 如何在亚马逊 EC2 上调试 python 网站?

    我是网络开发新手 这可能是一个愚蠢的问题 但我找不到可以帮助我的确切答案或教程 我工作的公司的网站 用 python django 构建 托管在亚马逊 EC2 上 我想知道从哪里开始调试这个生产站点并检查存储在那里的日志和数据库 我有帐户信
  • minizinc python 安装

    我通过 anaconda 提示符在 python 上安装了 minizinc 就像其他软件包一样 pip install minizinc 该软件包表示已成功安装 我可以导入该模块 但是 我正在遵循基本示例https minizinc py
  • Java 的 PriorityQueue 与最小堆有何不同?

    他们为什么命名PriorityQueue如果你不能插入优先级 它看起来与堆非常相似 有什么区别吗 如果没有区别那为什么叫它PriorityQueue而不是堆 默认的PriorityQueue是用Min Heap实现的 即栈顶元素是堆中最小的
  • 记录类名、方法名和行号的性能影响

    我正在我的 java 应用程序中实现日志记录 以便我可以调试应用程序投入生产后可能出现的潜在问题 考虑到在这种情况下 人们不会奢侈地使用 IDE 开发工具 以调试模式运行事物或单步执行完整代码 因此在每条消息中记录类名 方法名和行号将非常有
  • ArrayList.clear() 和 ArrayList.removeAll() 有什么区别?

    假如说arraylist定义为ArrayList
  • 如何使用通配符模拟泛型方法的行为

    我正在使用 EasyMock 3 2 我想基于 Spring Security 为我的部分安全系统编写一个测试 我想嘲笑Authentication http docs spring io autorepo docs spring secu
  • 如何访问我的 Android 程序中的联系人

    我正在制作一个短信应用程序 并且想要访问我的 Android 应用程序中的联系人 我想访问联系人 就像他们在实际联系人列表中一样 选择后 我需要返回到我的活动 在其中我可以向该人发送短信 或者是否可以访问存储联系人的数据库 我的代码如下所示

随机推荐