Android Kotlin权限管理最佳实践

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


一、基础封装思路

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

二、核心封装代码

kotlin 复制代码
// 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) multiplePermissionsContract
            else 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 false
        return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
    }
    // endregion
}

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

kotlin 复制代码
// 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
    )
}

四、业务层调用示例

kotlin 复制代码
// 在 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 处理策略

kotlin 复制代码
private fun showRationaleDialog() {
    MaterialAlertDialogBuilder(requireContext())
        .setTitle("需要权限")
        .setMessage("此功能需要相机和存储权限")
        .setPositiveButton("去授权") { _, _ ->
            // 再次请求权限
            requestPermissions(permissionsList, callback)
        }
        .setNegativeButton("取消", null)
        .show()
}
  1. 永久拒绝处理
kotlin 复制代码
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 埋点等)。

相关推荐
weixin_489690029 分钟前
【IDEA 2025.2.4】 Maven 仅能手动 Reload All Maven Projects 问题解决
java·maven·intellij-idea
雨辰AI9 分钟前
MySQL 迁移至达梦 DM9 完整改造指南|99% SQL 零改动
java·开发语言·数据库·sql·mysql·政务
golang学习记9 分钟前
Intellij IDEA 2026重磅更新!开发体验大升级
java·ide·intellij-idea
弹简特13 分钟前
【Java项目-轻聊】05-AI赋能设计接口文档
java·开发语言
达达爱吃肉22 分钟前
claude 接入deepseek 运行报错
java·服务器·前端
OctShop大型商城源码22 分钟前
OctShop对比JAVA商城源码_OctShop大型专业级多用户商城源码
java·开发语言·商城系统·小程序商城·octshop
guslegend26 分钟前
AGENT.md,Skill与工程规范
java·开发语言·数据库
jingling55529 分钟前
Flutter | Dio网络请求实战
android·开发语言·前端·flutter
周末也要写八哥30 分钟前
C++中单线程方式之无脑上锁
java·开发语言·c++
Reisentyan37 分钟前
[Advance]GoLang Learn Data Day 4
java·数据库·golang