Hello大家早上好,说起PermissionX,其实我已经有段时间没有更新这个框架了。一是因为现在工作确实比较忙,没有过去那么多的闲暇时间来写开源项目,二是因为,PermissionX的主体功能已经相当稳定,并不需要频繁对其进行变更。
不过之前一直有朋友在反映,对于Android中的一些特殊权限申请,PermissionX并不支持。是的,PermissionX本质上只是对Android运行时权限API进行了一层封装,用于简化运行时权限申请的。而这些特殊权限并不属于Android运行时权限的一部分,所以PermissionX自然也是不支持的。但是特殊权限却是我们这些开发者们可能经常要与之打交道的一部分,它们并不难写,但是每次去写都感觉很繁琐。因此经慎重考虑之后,我决定将几个比较常用的特殊权限纳入PermissionX的支持范围。那么本篇文章我们就来看一看,对于这几个常见的特殊权限,使用PermissionX和不使用PermissionX的写法有什么不同之处。对于还从来没有了解过PermissionX的朋友们,可以到我的公众号主页->历史文章->权限系列,查看我写的关于PermissionX的所有文章,将可以帮助你很好地理解和使用这个框架。事实上,Android的权限机制也是经历过长久的迭代的。在6.0系统之前,Google将权限机制设计的比较简单,你的应用程序需要用到什么权限,只需要在AndroidManifest.xml文件中声明一下就可以了。但是从6.0系统开始,Android引入了运行时权限机制。Android将常用的权限大致归成了几类,一类是普通权限,一类是危险权限,一类是特殊权限。普通权限指的是那些不会直接威胁到用户的安全和隐私的权限,这种权限和过去一样,只需要在AndroidManifest.xml文件中声明一下就可以了,不需要做任何特殊处理。危险权限则表示那些可能会触及用户隐私或者对设备安全性造成影响的权限,如获取设备联系人信息、定位设备的地理位置等。这部分权限需要通过代码进行申请,并要用户手动同意才可获得授权。PermissionX库主要就是处理的这种权限的申请。而特殊权限则更加少见,Google认为这种权限比危险权限还要敏感,因此不能仅仅让用户手动同意就可以获得授权,而是需要让用户到专门的设置页面去手动对某一个应用程序授权,该程序才能使用这个权限。不过相比于危险权限,特殊权限没有非常固定的申请方式,每个特殊权限可能都要使用不同的写法才行,这也导致申请特殊权限比申请危险权限还要繁琐。从1.5.0版本开始,PermissionX对最常用的几个特殊权限进行了支持。正如刚才所说,特殊权限没有固定的申请方式,因此PermissionX也是针对于这几个特殊权限一个一个去适配并支持的。如果你发现你需要申请的某个特殊权限还没有被PermissionX支持,也可以向我提出需求,我会考虑在接下来的版本中加入。目前的jcenter处在一个半废弃的边缘,虽然还可以正常从jcenter下载开源库,但是已经不能再向jcenter发布新的开源库了。而在明年2月1号之后,下载服务也会被关停。所以,以后要想再发布开源库我们只能选择发布到其他仓库,比如现在Google推荐我们使用Maven Central。于是,从1.5.0版本开始,PermissionX也会将库发布到Maven Center上,之前的老版本由于迁移价值并不大,所以我也不想再耗费经历做迁移了。1.5.0之前的版本仍然保留在jcenter上,提供下载服务直到明年的2月1号。而关于如何将库发布到Maven Central,我在准备接下来的几周里写一篇比较详细的文章,讲述完整的发布过程。说实话,这个我也不太清楚。我所了解的特殊权限基本都是因为需要用到了,然后发现这个权限即不属于普通权限,也不属于危险权限,要用一种更加特殊的方式去申请,才知道原来这是一个特殊权限。因此,PermissionX 1.5.0版本中对特殊权限的支持,也就仅限于我知道的,以及从网友反馈得来的几个最为常用的特殊权限。接下来我就分别针对这3个特殊权限做一下更加详细的介绍。悬浮窗功能在不少应用程序中使用得非常频繁,因为你可能总有一些内容是要置顶于其他内容之上显示的,这个时候用悬浮窗来实现就会非常方便。当然,如果你只是在自己的应用内部实现悬浮窗功能是不需要申请权限的,但如果你的悬浮窗希望也能置顶于其他应用程序的上方,这就必须得要申请权限了。悬浮窗的权限名叫做SYSTEM_ALERT_WINDOW,如果你去查一下这个权限的文档,会发现这个权限的申请方式比较特殊:按照文档上的说法,从Android 6.0系统开始,我们在使用SYSTEM_ALERT_WINDOW权限前需要发出一个action为Settings.ACTION_MANAGE_OVERLAY_PERMISSION的Intent,引导用户手动授权。另外我们还可以通过Settings.canDrawOverlays()这个API来判断用户是否已经授权。因此,想要申请悬浮窗权限,自然而然就可以写出以下代码:if (Build.VERSION.SDK_INT >= 23) {
if (Settings.canDrawOverlays(context)) {
showFloatView()
} else {
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
startActivity(intent)
}
} else {
showFloatView()
}
确实,但是它麻烦的点主要在于,它的请求方式是脱离于一般运行时权限的请求方式的,因此得要为它额外编写独立的权限请求逻辑才行。而PermissionX的目标就是要弱化这种独立的权限请求逻辑,减少差异化代码编写,争取使用同一套API来实现对特殊权限的请求。如果你已经比较熟悉PermissionX的用法了,那么以下代码你一定不会陌生:PermissionX.init(activity)
.permissions(Manifest.permission.SYSTEM_ALERT_WINDOW)
.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的正常用法,但是我们在这里却用来请求了悬浮窗权限。也就是说,即使是特殊权限,在PermissionX中也可以用普通的方式去处理。另外不要忘记,所有申请的权限都必须在AndroidManifest.xml进行注册才行:<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.permissionx.app">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
</manifest>
可以看到,PermissionX还自带了一个权限提示框,友好地告知用户我们需要悬浮窗权限,引导用户去手动开启。了解了悬浮窗权限的请求方式之后,接下来我们就可以快速过一下修改设置权限的请求方式了,因为它们的用法是完全一样的。修改设置的权限名叫WRITE_SETTINGS,如果我们去查看一下它的文档,你会发现它和刚才悬浮窗权限的文档简直如出一辙:同样是从Android 6.0系统开始,在使用WRITE_SETTINGS权限前需要先发出一个action为Settings.ACTION_MANAGE_WRITE_SETTINGS的Intent,引导用户手动授权。然后我们还可以通过Settings.System.canWrite()这个API来判断用户是否已经授权。所以,如果是自己手动申请这个权限,相信你已经知道要怎么写了。那么用PermissionX申请的话应该要怎么写呢?这个当然就更简单了,只需要把要申请的权限替换一下即可,其他部分都不用作修改:PermissionX.init(activity)
.permissions(Manifest.permission.WRITE_SETTINGS)
...
当然,不要忘记在AndroidManifest.xml中注册权限:<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.permissionx.app">
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
</manifest>
管理外部存储权限也是一种特殊权限,它可以允许你的App拥有对整个SD卡进行读写的权限。有些朋友可能会问,SD卡本来不就是可以全局读写的吗?为什么还要再申请这个权限?那你一定是没有了解Android 11上的Scoped Storage功能。从Android 11开始,Android系统强制启用了Scoped Storage,所有App都不再拥有对SD卡进行全局读写的权限了。但是如果有的应用就是要对SD卡进行全局读写该怎么办呢(比如说文件浏览器)?不用担心,Google仍然还是给了我们一种解决方案,那就是请求管理外部存储权限。这个权限是Android 11中新增的,为的就是应对这种特殊场景。那么这个权限要怎么申请呢?我们还是先来看一看文档: 在AndroidManifest.xml中声明MANAGE_EXTERNAL_STORAGE权限。
发出一个action为Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION的Intent,引导用户手动授权。
调用Environment.isExternalStorageManager()来判断用户是否已授权。
传统请求权限的写法我就不再演示了,使用PermissionX来请求的写法仍然也还是差不多的。只不过要注意,因为MANAGE_EXTERNAL_STORAGE权限是Android 11系统新加入的,所以我们也只应该在Android 11以上系统去请求这个权限,代码如下所示:if (Build.VERSION.SDK_INT >= 30) {
PermissionX.init(this)
.permissions(Manifest.permission.MANAGE_EXTERNAL_STORAGE)
...
}
AndroidManifest.xml中的权限如下:<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.permissionx.app">
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
</manifest>
另外PermissionX还有一个特别方便的地方,就是它可以一次性申请多个权限。假如我们想要同时申请悬浮窗权限和修改设置权限,只需要这样写就可以了:PermissionX.init(activity)
.permissions(Manifest.permission.SYSTEM_ALERT_WINDOW, Manifest.permission.WRITE_SETTINGS)
...
当然你也可以将特殊权限与普通运行时权限放在一起申请,PermissionX对此也是支持的。只有当所有权限都请求结束时,PermissionX才会将所有权限的请求结果一次性回调给开发者。关于PermissionX新版本的内容变化就介绍到这里,升级的方式非常简单,修改一下dependencies当中的版本号即可:repositories {
google()
mavenCentral()
}
dependencies {
implementation 'com.guolindev.permissionx:permissionx:1.5.0'
}
注意现在一定要使用mavenCentral仓库,而不能再使用jcenter了。如果你对PermissionX的源码感兴趣,可以访问PermissionX的项目主页:
https://github.com/guolindev/PermissionX
另外,本篇文章主要介绍的是PermissionX 1.5.0版本的新特性。如果你之前并没有接触过PermissionX,可以到我的公众号主页->历史文章->权限系列,查看我写的关于PermissionX的所有文章,里面有非常详尽的用法讲解。