[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)
        }
    }

}
相关推荐
雨白2 小时前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk2 小时前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING3 小时前
RN容器启动优化实践
android·react native
恋猫de小郭5 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker11 小时前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴11 小时前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭21 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe1 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农1 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos