前言
市面上权限请求的库很多,而前段时间官方刚刚将 requestPermissions()
+ onRequestPermissionsResult()
API 弃用,那么官方的替代方案是什么呢?本文将介绍 Activity Result API 进行权限请求的使用以及如何借助 Kotlin 扩展函数自己封装一个权限请求库
Activity Result API
在 Android Jetpack Activity 1.2.0-alpha02
和 Fragment 1.3.0-alpha02
中,Google 提供了全新的 Activity Result API 来替换 startActivityForResult()
+ onActivityResult()
和 requestPermissions()
+ onRequestPermissionsResult()
。详情可移步 官方文档[1],中文可以参考 秉心说[2] 的 是时候丢掉 onActivityResult 了 ![3],有些 API 的名字发生了变化,请留意
requestPermissions() / onRequestPermissionsResult() 被弃用
紧接着在 Activity 1.2.0-alpha04
和 Fragment 1.3.0-alpha04
版本中,
startActivityForResult()
+onActivityResult()
和requestPermissions()
+onRequestPermissionsResult()
被标记为弃用,而在Fragment 1.3.0-alpha05
这些标记弃用的方法内部已改为使用 ActivityResultRegistry
实现
新 API 的使用
新的 API 使用非常简单,分为单一权限请求,和多权限请求,Activity 和 Fragment 使用方法相同
单一权限请求
val permission = Manifest.permission.WRITE_EXTERNAL_STORAGE
registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
// 请求结果,result 为 boolean true 代表已授权,false 代表未授权
}.launch(permission)
多权限请求
val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA)
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result: MutableMapBoolean> ->// 请求结果,返回一个map ,其中 key 为权限名称,value 为是否权限是否赋予
}.launch(permissions)
配合 Kotlin 扩展函数进行封装
配合 Kotlin 的扩展函数,我们可以将权限请求的逻辑进行封装。
开发过程中,我们申请权限时关注的就是权限是否申请成功,如果未申请成功是否勾选了不再询问
因此我们可以加入「权限申请成功」,「权限申请失败且未勾选不再询问」,「权限申请失败且已勾选不再询问」三种状态的回调
/**
* [permission] 权限名称
* [granted] 申请成功
* [denied] 被拒绝且未勾选不再询问
* [explained] 被拒绝且勾选不再询问
*/
inline fun Fragment.requestPermission(
permission: String,crossinline granted: (permission: String) -> Unit = {},crossinline denied: (permission: String) -> Unit = {},crossinline explained: (permission: String) -> Unit = {}
) {
registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
when {
result -> granted.invoke(permission)
shouldShowRequestPermissionRationale(permission) -> denied.invoke(permission)
else -> explained.invoke(permission)
}
}.launch(permission)
}
/**
* [permissions] 权限数组
* [allGranted] 所有权限均申请成功
* [denied] 被拒绝且未勾选不再询问,同时被拒绝且未勾选不再询问的权限列表
* [explained] 被拒绝且勾选不再询问,同时被拒绝且勾选不再询问的权限列表
*/
inline fun Fragment.requestMultiplePermissions(vararg permissions: String,crossinline allGranted: () -> Unit = {},crossinline denied: (List<String>) -> Unit = {},crossinline explained: (List<String>) -> Unit = {}
) {
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result: MutableMapBoolean> ->//过滤 value 为 false 的元素并转换为 listval deniedList = result.filter { !it.value }.map { it.key }when {
deniedList.isNotEmpty() -> {//对被拒绝全选列表进行分组,分组条件为是否勾选不再询问val map = deniedList.groupBy { permission ->if (shouldShowRequestPermissionRationale(permission)) DENIED else EXPLAINED
}//被拒接且没勾选不再询问
map[DENIED]?.let { denied.invoke(it) }//被拒接且勾选不再询问
map[EXPLAINED]?.let { explained.invoke(it) }
}else -> allGranted.invoke()
}
}.launch(permissions)
}
使用起来是这样的
单一权限申请
requestPermission(Manifest.permission.RECORD_AUDIO,
granted = { permission ->
//权限申请成功
},
denied = { permission ->
//权限申请失败且未勾选不再询问,下次可继续申请
},
explained = { permission ->
//权限申请失败且已勾选不再询问,需要向用户解释原因并引导用户开启权限
})
多权限申请
requestMultiplePermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA,
allGranted = {
//全部权限均已申请成功
},
denied = {list->
//权限申请失败且未勾选不再询问,下次可继续申请
},
explained = {list->
//权限申请失败且已勾选不再询问,需要向用户解释原因并引导用户开启权限
})
Java 版本
Java 是可以调用 Kotlin 的扩展函数的,为了更方便地调用,可以在此基础上再封装一层
单一权限请求
PermissionUtils.requestPermission(this, permission, new PermissionResultListener() {
@Override
public void granted(String permission) {
Log.i(TAG, "granted: ");
}
@Override
public void denied(String permission) {
Log.i(TAG, "denied: ");
}
@Override
public void explained(String permission) {
Log.i(TAG, "explained: ");
}
});
多权限请求
PermissionUtils.requestMultiplePermissions(this, new MultiPermissionResultListener() {
@Override
public void allGranted() {
Log.i(TAG, "allGranted: ");
}
@Override
public void denied(List list) {
Log.i(TAG, "denied: " + list.toString());
}
@Override
public void explained(List list) {
Log.i(TAG, "explained: " + list.toString());
}
}, permissions);
项目地址
demo 在这里[4],如果感觉这个思路对你有帮助的话,点一颗小星星吧~ ?
另外我还将它传到了 JitPack 上,现已支持 Kotlin DSL 写法,引入姿势如下:
在项目根目录的
build.gradle
加入allprojects {
repositories {
//...
maven { url 'https://jitpack.io' }
}
}
添加依赖
dependencies {
implementation 'com.github.Flywith24:Flywith24-Permission:$version'
}
关于我
我是 Flywith24[5],我的博客内容已经分类整理 在这里[6],点击右上角的 Watch 可以及时获取我的文章更新哦 ?
掘金[7]
简书[8]
Github[9]
参考资料
[1]官方文档: https://developer.android.com/training/basics/intents/result
[2]秉心说: https://juejin.im/user/586eff908d6d81005879507d
[3]是时候丢掉 onActivityResult 了 !: https://juejin.im/post/5e80cb1ee51d45471654fae7
[4]demo 在这里: https://github.com/Flywith24/Flywith24-Permission
[5]Flywith24: https://flywith24.gitee.io/
[6]在这里: https://github.com/Flywith24/BlogList
[7]掘金: https://juejin.im/user/57c7f6870a2b58006b1cfd6c
[8]简书: https://www.jianshu.com/u/3d5ad6043d66
[9]Github: https://github.com/Flywith24