开源项目:使用 Activity Result API + Kotlin 扩展函数 封装权限请求库

    技术2022-07-12  82

    前言

    市面上权限请求的库很多,而前段时间官方刚刚将 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: MutableMap<String, Boolean> -> // 请求结果,返回一个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: MutableMap<String, Boolean> ->         //过滤 value 为 false 的元素并转换为 list         val 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<String> list) {         Log.i(TAG, "denied: " + list.toString());     }     @Override     public void explained(List<String> 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

    Processed: 0.010, SQL: 9