一、动态权限封装与拦截器架构
1. 权限拦截器核心设计(示例代码)
拦截器目标:在权限申请前增加统一拦截逻辑(如展示使用说明弹窗),提升用户授权率。
kotlin
// 权限拦截器接口
interface PermissionInterceptor {
fun shouldIntercept(context: Context, permissions: List<String>): Boolean
fun intercept(context: Context, permissions: List<String>, callback: () -> Unit)
}
// 示例:首次申请权限时展示头部弹窗的拦截器
class HeaderTipInterceptor : PermissionInterceptor {
private val sp = context.getSharedPreferences("permission_prefs", MODE_PRIVATE)
override fun shouldIntercept(context: Context, permissions: List<String>): Boolean {
return permissions.any { !sp.getBoolean(it, false) }
}
override fun intercept(context: Context, permissions: List<String>, callback: () -> Unit) {
AlertDialog.Builder(context)
.setTitle("功能权限说明")
.setMessage("我们需要以下权限以正常使用功能:\n${formatPermissions(permissions)}")
.setPositiveButton("去申请") { _, _ ->
// 标记权限已展示过说明
permissions.forEach { sp.edit().putBoolean(it, true).apply() }
callback()
}
.setNegativeButton("取消", null)
.show()
}
private fun formatPermissions(permissions: List<String>): String {
return permissions.joinToString("\n") { when (it) {
Manifest.permission.CAMERA -> "- 相机:用于拍照和视频通话"
Manifest.permission.ACCESS_FINE_LOCATION -> "- 位置:提供周边服务推荐"
else -> "- ${it.substringAfterLast('.')}"
}}
}
}
2. 增强版权限工具类(集成拦截器:示例代码)
kotlin
object PermissionManager {
private val interceptors = mutableListOf<PermissionInterceptor>()
// 添加拦截器
fun addInterceptor(interceptor: PermissionInterceptor) {
interceptors.add(interceptor)
}
// 执行权限请求(带拦截逻辑)
fun requestPermissions(
activity: FragmentActivity,
permissions: Array<String>,
onResult: (granted: List<String>, denied: List<String>) -> Unit
) {
val filteredPermissions = permissions.filter {
ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED
}
if (filteredPermissions.isEmpty()) {
onResult(permissions.toList(), emptyList())
return
}
// 检查是否需要拦截
val interceptor = interceptors.firstOrNull { it.shouldIntercept(activity, filteredPermissions) }
if (interceptor != null) {
interceptor.intercept(activity, filteredPermissions) {
realRequest(activity, filteredPermissions, onResult)
}
} else {
realRequest(activity, filteredPermissions, onResult)
}
}
private fun realRequest(
activity: FragmentActivity,
permissions: List<String>,
onResult: (List<String>, List<String>) -> Unit
) {
val launcher = activity.registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { grants ->
val granted = grants.filter { it.value }.keys.toList()
val denied = grants.filter { !it.value }.keys.toList()
onResult(granted, denied)
}
launcher.launch(permissions.toTypedArray())
}
}
二、使用示例
1. 初始化拦截器(示例代码)
kotlin
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
PermissionManager.addInterceptor(HeaderTipInterceptor())
PermissionManager.addInterceptor(BackgroundLocationInterceptor()) // 其他拦截器
}
}
2. 在Activity中请求权限(示例代码)
kotlin
class CameraActivity : AppCompatActivity() {
private val PERMISSIONS = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
)
fun openCamera() {
PermissionManager.requestPermissions(
activity = this,
permissions = PERMISSIONS,
onResult = { granted, denied ->
if (granted.containsAll(PERMISSIONS.toList())) {
startCamera()
} else {
handleDeniedPermissions(denied)
}
}
)
}
private fun handleDeniedPermissions(denied: List<String>) {
if (denied.any { !ActivityCompat.shouldShowRequestPermissionRationale(this, it) }) {
showGoSettingsDialog()
} else {
showRetryDialog()
}
}
}
三、注意事项与兼容性处理
1. 版本兼容关键点
Android 版本 | 处理策略 |
---|---|
< 6.0 (API 23) | 直接授予清单中声明的权限 |
6.0-10 (API 23-29) | 标准动态权限申请流程 |
11+ (API 30+) | 处理单次授权、后台位置权限 |
13+ (API 33+) | 细化媒体权限(READ_MEDIA_IMAGES)、附近设备权限(NEARBY_WIFI_DEVICES) |
2. 特殊权限处理(示例代码)
-
后台定位权限:
kotlinclass BackgroundLocationInterceptor : PermissionInterceptor { override fun shouldIntercept(context: Context, permissions: List<String>): Boolean { return permissions.contains(Manifest.permission.ACCESS_BACKGROUND_LOCATION) && ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PERMISSION_GRANTED } override fun intercept(context: Context, permissions: List<String>, callback: () -> Unit) { AlertDialog.Builder(context) .setTitle("需要后台位置权限") .setMessage("请允许应用在后台访问位置,以持续提供导航服务") .setPositiveButton("去设置") { _, _ -> context.startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { data = Uri.fromParts("package", context.packageName, null) }) } .setNegativeButton("取消", null) .show() } }
3. 用户体验优化
- 权限分组提示:同一权限组的权限合并说明
- 智能重试策略:用户拒绝后,根据拒绝次数决定展示说明的频率
- 场景化申请:按功能触发权限申请(如进入拍照界面时申请相机权限)
四、高级功能扩展
1. 权限申请日志追踪(示例代码)
kotlin
class AnalyticsInterceptor : PermissionInterceptor {
override fun shouldIntercept(context: Context, permissions: List<String>) = false
override fun intercept(context: Context, permissions: List<String>, callback: () -> Unit) {
// 上报权限申请事件
Firebase.analytics.logEvent("permission_request", bundleOf(
"permissions" to permissions.joinToString(",")
))
callback()
}
}
2. A/B测试不同提示策略(示例代码)
kotlin
class A/BTestInterceptor : PermissionInterceptor {
private val variant = RemoteConfig.getInstance().getString("permission_ui_variant")
override fun shouldIntercept(context: Context, permissions: List<String>): Boolean {
return variant != "control"
}
override fun intercept(context: Context, permissions: List<String>, callback: () -> Unit) {
when (variant) {
"variant1" -> showMaterialDialog(context, callback)
"variant2" -> showBottomSheet(context, callback)
}
}
}
五、完整兼容性方案实践
1. 版本适配工具类(示例代码)
kotlin
object PermissionCompat {
// 检查是否需要特殊处理
fun needSpecialHandle(permission: String): Boolean {
return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
permission == Manifest.permission.ACCESS_BACKGROUND_LOCATION -> true
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
permission == Manifest.permission.READ_MEDIA_IMAGES -> true
else -> false
}
}
// 转换旧权限到新权限
fun convertLegacyPermissions(permissions: Array<String>): Array<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissions.map {
if (it == Manifest.permission.READ_EXTERNAL_STORAGE) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
it
}
}.toTypedArray()
} else {
permissions
}
}
}
六、总结
通过 拦截器模式 实现的动态权限管理具备以下优势:
- 高可扩展性:轻松添加埋点、A/B测试等新功能
- 统一入口:集中处理权限相关逻辑,降低耦合
- 用户体验可控:通过拦截器灵活控制提示策略
- 兼容性保障:通过版本适配层处理系统差异
建议结合 Jetpack Security 和 Biometric API 构建完整的应用安全体系。