[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去存储对应的关系:

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

注册启动器 & 注销注册器

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

    //注册启动器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则会自动将二次映射的数据项进行移除。注销注册器的代码如下所示:

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

完整代码

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

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

Update|更新

使用示例

class MainActivity : AppCompatActivity() {val viewBinding by lazy {ActivityMainBinding.inflate(layoutInflater)}init {//申请权限注册PermissionUtil.registerLauncher(this,Manifest.permission.READ_EXTERNAL_STORAGE)}......
}

2024.1.18

为了优化权限申请的流程,直接将启动器申请权限的部分通过Lifecycle注入到了宿主的生命周期中,这样在注册完启动器之后就无需再手动调用方法申请权限,更新后的完整代码:

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_CREATE) {PermissionUtil.requestPermission(owner,permission)}//注册一个事件自动移除启动器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)}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/417720.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决 conda新建虚拟环境只有一个conda-meta文件&conda新建虚拟环境不干净

像以前一样通过conda 新建虚拟环境时发现环境一团糟&#xff0c;首先新建虚拟环境 conda create -n newenv这时候activate newenv&#xff0c;通过pip list&#xff0c;会发现有很多很多的包&#xff0c;都是我在其他环境用到的。但诡异的是&#xff0c;来到anaconda下env的目…

福昕软件的使用

快捷操作 快捷键 快捷键功能备注Ctrl P打印 Ctrl W关闭 Ctrl B书签 鼠标放菜单栏&#xff0c;单击右键即可导入/导出 自定义菜单栏文件-->偏好设置-->文档 1、多实例&#xff1a;单击PDF后均重新打开一个新界面。

【51单片机Keil+Proteus8.9+ADC0804】ADC实验 模拟转数字实验

一、实验名称 ADC实验 模拟转数字实验 二、设计思路 电路设计 1.选用AT89C51单片机作为电路核心单元&#xff0c;外接8位单通道AD转换器ADC0804芯片和LM016L显示器以及滑动变阻器等其它常用元器件构成电路。 2.将ADC0804芯片的控制引脚RD,WR,INTR接到AT89C51芯片对应引脚&…

SpringCloud-高级篇(十四)

缓存的作用是其实就是为了减轻对数据库的压力&#xff0c;缩短服务响应的时间&#xff0c;从而提高整个服务的并发能力&#xff0c;Redis单节点并发其实已经很高了&#xff0c;但是它依然有自己的上限&#xff0c;随着互联网的发展&#xff0c;用户低量越来越大&#xff0c;想淘…

51单片机原理及应用张毅刚版课后习题以及答案

AT89S51单片机内部集成了哪些外围功能部件 ①8位微处理器CPU ②数据存储器 128B RAM ③程序存储器 ④4个8位可编程并行I/O口 ⑤1个全双工的异步串行口 ⑥2个可编程的16位定时器/计数器 ⑦1个看门狗定时器WDT ⑧中断系统具有五个中断源 五个中断向量 ⑨特殊功能寄存器SFR 26个…

vulnhub靶机BlueSky

下载地址&#xff1a;BlueSky: 1 ~ VulnHub 主机发现 目标177 端口扫描 服务扫描 漏洞扫描 看web就不用我多说了吧 默认页面&#xff0c;爆破吧 这个也没有扫出来 有manger/html但是没有任何返回值 应该是限制本地访问或者禁掉了 网上直接用struts2-showcase这个洞了 cve-20…

智能AI写作到底怎么样?这几款AI写作非常好用

近年来&#xff0c;人工智能&#xff08;AI&#xff09;技术的快速发展已经渗透到各个领域&#xff0c;包括写作领域。AI写作软件通过模仿指定作家的风格和语言&#xff0c;能够生成高质量的文章。这种技术的出现引发了广泛的讨论和争议。本文将探讨AI写作的优点&#xff0c;并…

flutter获取地理定位:geolocator依赖详细用法

本文使用geolocator插件实现app物理定位功能。 该插件的主要功能有&#xff1a; 获取最后已知位置&#xff1b;获取设备当前位置&#xff1b;获取连续的位置更新&#xff1b;检查设备是否启用了定位服务&#xff1b;计算两个地理坐标之间的距离&#xff08;米&#xff09;&am…

air001研究笔记.基于arduino快速开发简单项目

一、air001芯片简介 air001是厂商合宙推出的一款tssop封装的mcu芯片。支持swd与串口烧录&#xff0c;多面向简单的功能简单类别的电子产品&#xff0c;因为官方文档齐全上手简易&#xff0c;所以也特别适合非专业爱好者乃至于幼儿编程。芯片内置资源&#xff1a;AIR001芯片数据…

家教上门助教小程序源码,家教小程序,家教系统,家教app,家教源码

家教上门助教小程序源码&#xff0c;家教小程序&#xff0c;家教系统&#xff0c;家教app&#xff0c;家教源码 推荐使用宝塔面板Linux NginxPHPMYSQL 支持家教老师筛选 支持家教人员入住 支持购买课程 支持教学资讯 支持订单课程

项目管理十大知识领域之项目沟通管理

一、项目沟通管理概述 项目沟通管理是项目管理中的重要组成部分&#xff0c;它涉及到对项目信息的收集、处理、存档和传递。一个成功的项目沟通管理可以确保团队成员、利益相关者以及其他相关方之间的信息交流畅通无阻&#xff0c;从而推动项目顺利进行。沟通管理涉及的内容不…

【微信小程序开发】环境介绍和基本使用

文章目录 前言1. 项目的基本组成结构1.1 JSON 配置文件的作用1.2 如何新建小程序页面1.3 修改项目首页1.4 WXML 模板1.5 WXSS 样式1.6 JS 逻辑交互 2. 宿主环境2.1 什么是宿主环境2.2 通信模型2.3 运行机制2.4 组件2.4.1 view 组件的基本使用&#xff1a;2.4.2 scroll-view 组件…