Android Kotlin权限管理最佳实践

发布于:2025-05-20 ⋅ 阅读:(15) ⋅ 点赞:(0)

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

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

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


网站公告

今日签到

点亮在社区的每一天
去签到