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

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 埋点等)。

相关文章:

  • 看之前熟悉双亲委派加载机制,看之后了解双亲委派加载机制
  • 最大子树和--树形dp
  • Linux基础第四天
  • fastadmin 数据导出,设置excel行高和限制图片大小
  • 在Excel中使用函数公式时,常见错误对应不同的典型问题
  • Python学习笔记--使用Django操作mysql
  • 一键启动多个 Chrome 实例并自动清理的 Bash 脚本分享!
  • AWS EKS IP 耗尽:原因、解决方案和最佳实践
  • 【AWS入门】AWS身份验证和访问管理(IAM)
  • 【Windows系统】向量数据库Milvus安装教程
  • Win10 安装单机版ES(elasticsearch),整合IK分词器和安装Kibana
  • 鸿蒙PC操作系统:从Linux到自研微内核的蜕变
  • 手机内存不够,哪些文件可以删?
  • 小红书的视频怎么保存没有水印(方法分享)
  • linux——mysql故障排查与生产环境优化
  • Python打卡DAY30
  • MySQL函数触发:函数处理与触发器自动化应用
  • Node 服务监控及通过钉钉推送告警提醒
  • 实战教程:影刀RPA采集闲鱼商品并分享钉钉
  • Linux内核深入学习(4)——内核常见的数据结构2——红黑树
  • 长沙至赣州高铁初步设计获批,可填补湘赣两省斜向交通空白
  • 专访|金七猫奖得主:以非遗为舟,在现实题材中疗愈与成长
  • 新华社千笔楼:地方文旅宣传应走出“魔性尬舞”的流量焦虑
  • 浙江广厦:诚挚道歉,涉事责任人交公安机关
  • 博物馆书单|走进博物馆,去体验一场与文明的对话
  • 15年全程免费,内蒙古准格尔旗实现幼儿园到高中0学费