当前位置: 首页 > backend >正文

Android Kotlin权限管理最佳实践

在 Android 开发中,权限管理是保证应用安全性和用户体验的重要环节。以下是基于 Kotlin 的权限管理封装最佳实践,采用现代 API 和简洁的代码结构:


一、基础封装思路

  1. 使用 Activity Result API(替代 onRequestPermissionsResult
  2. 结合 Kotlin 扩展函数和高阶函数简化调用
  3. 统一处理权限请求结果(包括拒绝/永久拒绝)
  4. 支持多权限请求和单权限请求

二、核心封装代码

// PermissionManager.kt
object PermissionManager {// region ========== Contracts ==========private val multiplePermissionsContract =ActivityResultContracts.RequestMultiplePermissions()private val singlePermissionContract =ActivityResultContracts.RequestPermission()// endregion// region ========== Result Types ==========sealed class PermissionResult {object Granted : PermissionResult()data class Denied(val permissions: List<String>) : PermissionResult()data class PermanentlyDenied(val permissions: List<String>) : PermissionResult()}// endregion// region ========== Public Methods ==========fun checkPermissions(lifecycleOwner: LifecycleOwner,registry: ActivityResultRegistry,permissions: List<String>,onResult: (PermissionResult) -> Unit) {when {// 已有全部权限permissions.all { isGranted(it) } -> {onResult(PermissionResult.Granted)}// 需要请求权限else -> launchPermissionRequest(lifecycleOwner,registry,permissions,onResult)}}// endregion// region ========== Private Methods ==========private fun launchPermissionRequest(lifecycleOwner: LifecycleOwner,registry: ActivityResultRegistry,permissions: List<String>,onResult: (PermissionResult) -> Unit) {val launcher = registry.register("permission_request_key",lifecycleOwner,if (permissions.size > 1) multiplePermissionsContractelse singlePermissionContract) { results ->handlePermissionResult(results, permissions, onResult)}launcher.launch(permissions.toTypedArray())}private fun handlePermissionResult(results: Map<String, Boolean>,requestedPermissions: List<String>,onResult: (PermissionResult) -> Unit) {val deniedPermissions = results.filter { !it.value }.keys.toList()val permanentlyDenied = deniedPermissions.filter { !shouldShowRequestPermissionRationale(it) }when {deniedPermissions.isEmpty() -> onResult(PermissionResult.Granted)permanentlyDenied.isNotEmpty() -> onResult(PermissionResult.PermanentlyDenied(permanentlyDenied))else -> onResult(PermissionResult.Denied(deniedPermissions))}}private fun isGranted(permission: String): Boolean {return ContextCompat.checkSelfPermission(ApplicationProvider.getApplicationContext(),permission) == PackageManager.PERMISSION_GRANTED}private fun shouldShowRequestPermissionRationale(permission: String): Boolean {val activity = getTopActivity() ?: return falsereturn ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)}// endregion
}

三、使用扩展函数简化调用

// Activity/Fragment Extensions
fun Fragment.requestPermissions(permissions: List<String>,onResult: (PermissionManager.PermissionResult) -> Unit
) {PermissionManager.checkPermissions(viewLifecycleOwner,requireActivity().activityResultRegistry,permissions,onResult)
}fun Activity.requestPermissions(permissions: List<String>,onResult: (PermissionManager.PermissionResult) -> Unit
) {PermissionManager.checkPermissions(this,activityResultRegistry,permissions,onResult)
}

四、业务层调用示例

// 在 Activity/Fragment 中调用
requestPermissions(listOf(Manifest.permission.CAMERA,Manifest.permission.READ_EXTERNAL_STORAGE
)) { result ->when (result) {is PermissionManager.PermissionResult.Granted -> {// 权限已授予openCamera()}is PermissionManager.PermissionResult.Denied -> {// 部分权限被拒绝(非永久)showRationaleDialog()}is PermissionManager.PermissionResult.PermanentlyDenied -> {// 权限被永久拒绝showSettingsRedirectDialog()}}
}

五、最佳实践要点

  1. 分层处理逻辑

    • UI 层:处理弹窗提示和用户交互
    • 业务层:处理权限相关业务逻辑
    • 框架层:处理系统 API 交互
  2. Rationale 处理策略

private fun showRationaleDialog() {MaterialAlertDialogBuilder(requireContext()).setTitle("需要权限").setMessage("此功能需要相机和存储权限").setPositiveButton("去授权") { _, _ ->// 再次请求权限requestPermissions(permissionsList, callback)}.setNegativeButton("取消", null).show()
}
  1. 永久拒绝处理
private fun showSettingsRedirectDialog() {MaterialAlertDialogBuilder(requireContext()).setTitle("权限被永久拒绝").setMessage("请到设置中手动开启权限").setPositiveButton("去设置") { _, _ ->Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {data = Uri.fromParts("package", requireContext().packageName, null)startActivity(this)}}.setNegativeButton("取消", null).show()
}

六、注意事项

  1. 清单声明:仍需在 AndroidManifest.xml 中声明权限
  2. 结果回调:通过 Activity Result API 自动处理生命周期
  3. 配置变化:系统自动处理屏幕旋转等配置变化
  4. 权限分组:实际仍需单独检查每个权限状态

通过这种封装方式,可以实现以下优势:

  • 类型安全的权限结果处理
  • 与生命周期自动绑定
  • 支持 Fragment 和 Activity 统一调用
  • 清晰的拒绝状态区分
  • 避免内存泄漏风险

建议根据项目需求进一步扩展功能(如日志记录、Analytics 埋点等)。

http://www.xdnf.cn/news/7313.html

相关文章:

  • 【集成电路】集成电路导论知识点
  • HJ10 字符个数统计【牛客网】
  • JavaScript:PC端特效--缓动动画
  • Linux问题排查-找到偷偷写文件的进程
  • Word2Vec详解
  • 【Canvas与图标】圆角方块蓝星CSS图标
  • python打卡训练营打卡记录day30
  • 会议动态|第十五届亚太燃烧学术年会精彩探析
  • 解释:神经网络
  • 深入理解 ZAB:ZooKeeper 原子广播协议的工作原理
  • 26.项目集群-redis分布式锁
  • 力扣每日一题5-19
  • es在已有历史数据的文档新增加字段操作
  • 27.第二阶段x64游戏实战-分析技能属性
  • mysql故障排查与环境优化
  • DeepSeek 赋能数字孪生:重构虚实共生的智能未来图景
  • 【AI面试秘籍】| 第17期:MoE并行策略面试全攻略:从理论到调参的降维打击指南
  • 视觉-语言导航:综述与类别
  • 面试点补充
  • 【Vue】路由2——编程式路由导航、 两个新的生命周期钩子 以及 路由守卫、路由器的两种工作模式
  • 在Excel中使用函数公式时,常见错误对应不同的典型问题
  • 在 CentOS 7.9 上部署 node_exporter 并接入 Prometheus + Grafana 实现主机监控
  • 【Arm】应用ArmDS移植最小FreeRTOS系统
  • 利用ffmpeg截图和生成gif
  • FART 主动调用组件设计和源码分析
  • 【AWS入门】AWS身份验证和访问管理(IAM)
  • C#接口的setter或getter的访问性限制
  • ## Docker 中 Elasticsearch 启动失败:日志文件权限问题排查与解决
  • PYTHON训练营DAY30
  • Git基础面试题