[Android]实现一个权限申请类

[Android]实现一个权限申请类

导言

在引入了动态权限申请之后,Android的权限申请就变得尤为繁琐,若是按照原有的方法一板一眼地进行申请,样板代码未免太多。因此本篇文章就使用ActivityResult API,来实现一个简单的权限申请类来帮助我们简化权限申请的过程。

在此之前,你可能需要了解一些关于ActivityResult API相关的知识:使用Activity Result API 清爽地请求权限 和 启动Activity

前置知识

在介绍正式的实现过程之前还是先简单介绍一下ActivityResult API,这是AndroidX中的内容,旨在解耦Activity的职责,避免Activity中出现的大量的集中的回调代码的出现。

该API有两个重要的概念:Launcher(启动器),Contract(契约)。其中,启动器是真正帮我们去执行操作(比如申请权限)的类,而契约类则规定了启动器的行为,比如说申请权限还是启动另一个Activity。但是在我们今天的这个主题下,只需要关注申请权限这一个行为就OK了,所以也无需太过关注契约类。

除此之外,还需要注意的一点是ActivityResult 的启动器 必须要在宿主的生命周期到达 CREATED 之前就创建完毕,所以我们在设计申请类的时候必须把这一点考虑进去。

具体实现

权限检查

首先我们从简单地做起,一个权限相关的工具类必备的就是权限的检查,这个我们用原有的方法就可以了,代码如下:

复制代码
    //检查权限
    fun checkPermission(context:Context,target:Array<String>):Boolean {
        for (permission in target) {
            val result = ContextCompat.checkSelfPermission(context,permission)
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false
            }
        }
        return true
    }

核心设计

关于权限申请的权限的设计,由于有Launcher的存在,所以我们有存储Launcher的诉求,自然而然我们会想到用一张HashMap去存储。之后我们需要考虑的就是如何将权限,宿主。启动器这三者映射起来,所以可以用一张二维的HashMap去存储。具体结构是这样:一个宿主对应一张HashMap,该HashMap中一个权限对应一个Launcher,如下图所示:

我们用这么一个HashMap去存储对应的关系:

kotlin 复制代码
private val ownerMap = mutableMapOf<ComponentActivity,MutableMap<String,ActivityResultLauncher<String>>>()

注册启动器 & 注销注册器

有了上边所说的Map来存储映射关系之后,我们就需要填充这张Map了,具体来说就是注册启动器的过程,这个过程也十分简单,主要就是依次找对应项,如果对应项不存在的话就新创建一项进行填充:

kotlin 复制代码
    //注册启动器
    fun registerLauncher(owner: ComponentActivity, permission:String, callback:ActivityResultCallback<Boolean> = Empty_Callback) {
        val launcher = owner.registerForActivityResult(ActivityResultContracts.RequestPermission(),callback)
        if (!ownerMap.containsKey(owner)) {
            ownerMap[owner] = mutableMapOf()
        }
        ownerMap[owner]!!.put(permission,launcher)

        //注册一个事件自动移除启动器
        owner.lifecycle.addObserver(object :LifecycleEventObserver{
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    unRegisterLauncher(owner,permission)
                    Log.d(TAG, "onStateChanged: Removed")
                }

            }
        })
    }

这里比较特殊的一点是为了防止内存泄漏的问题,若宿主的生命周期已经达到onDestroy则会自动将二次映射的数据项进行移除。注销注册器的代码如下所示:

kotlin 复制代码
    //注销启动器
    fun unRegisterLauncher(context: Context,permission: String) {
        if (ownerMap.containsKey(context) && ownerMap[context]!!.containsKey(permission)) {
            ownerMap[context]!!.remove(permission)
        }
    }

完整代码

最后,给出完整的代码,如下所示:

kotlin 复制代码
object PermissionUtil {
    //若不需要回调,使用此空实现
    public val Empty_Callback = object :ActivityResultCallback<Boolean> {
        override fun onActivityResult(result: Boolean) {

        }
    }

    private const val TAG = "PermissionUtil"

    private val ownerMap = mutableMapOf<ComponentActivity,MutableMap<String,ActivityResultLauncher<String>>>()

    //注册启动器
    fun registerLauncher(owner: ComponentActivity, permission:String, callback:ActivityResultCallback<Boolean> = Empty_Callback) {
        val launcher = owner.registerForActivityResult(ActivityResultContracts.RequestPermission(),callback)
        if (!ownerMap.containsKey(owner)) {
            ownerMap[owner] = mutableMapOf()
        }
        ownerMap[owner]!!.put(permission,launcher)

        //注册一个事件自动移除启动器
        owner.lifecycle.addObserver(object :LifecycleEventObserver{
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    unRegisterLauncher(owner,permission)
                    Log.d(TAG, "onStateChanged: Removed")
                }

            }
        })
    }

    //申请权限
    fun requestPermission(context: Context,permission: String):Boolean {
        val launcher = ownerMap[context]?.get(permission)
        if (launcher == null) {
            Log.e(TAG, "Launcher 未注册!")
            return false
        } else {
            launcher.launch(permission)
            Log.d(TAG, "requestPermission: 请求ing")
            return (checkPermission(context, arrayOf(permission)))
        }
    }

    //检查权限
    fun checkPermission(context:Context,target:Array<String>):Boolean {
        for (permission in target) {
            val result = ContextCompat.checkSelfPermission(context,permission)
            if (result != PackageManager.PERMISSION_GRANTED) {
                return false
            }
        }
        return true
    }

    //注销启动器
    fun unRegisterLauncher(context: Context,permission: String) {
        if (ownerMap.containsKey(context) && ownerMap[context]!!.containsKey(permission)) {
            ownerMap[context]!!.remove(permission)
        }
    }

}
相关推荐
jinanwuhuaguo21 小时前
(第三十三篇)五月的文明奠基:OpenClaw 2026.5.2版本的文明级解读
android·java·开发语言·人工智能·github·拓扑学·openclaw
千码君20161 天前
Trae:一些关于flutter和 go前后端开发构建的分享
android·flutter·gradle·android-studio·trae·vibe code
重生之我是Java开发战士1 天前
【MySQL】事务 & 用户与权限管理
android·数据库·mysql
怣疯knight1 天前
Windows不安装 Android Studio如何打包安卓软件
android·windows·android studio
ke_csdn1 天前
从Java演变到Kotlin下的jet pack
android
wenzhangli71 天前
在低代码设计中践行 Harness Engineering
android·低代码·rxjava
xingpanvip1 天前
星盘接口开发文档:组合三限盘接口指南
android·开发语言·前端·python·php·lua
TechMix1 天前
【fkw学习笔记】Android 13 AOSP 源码添加系统预置应用实战指南
android·笔记·学习
云起SAAS1 天前
私域直播系统UniApp源码 多商户商城+直播带货 微信小程序+H5+安卓iOS
android·微信小程序·uni-app·私域直播系统
空中海1 天前
01. 安卓逆向基础、环境搭建与授权
android