背景
从 Android 6.0 (API 23) 开始,Google 引入了运行时权限模型。不再是在安装时"一键同意",而是需要开发者在运行时动态请求。Android 11+ 之后权限机制愈发严格,了解权限管理是每个 Android 开发者必备技能。
一、核心概念
权限分类
- 普通权限(Normal) :安装时默认授予,如
INTERNET、ACCESS_NETWORK_STATE,只需在 Manifest 中声明 - 危险权限(Dangerous):涉及隐私数据,如相机、位置、联系人、存储,必须运行时动态请求
- 特殊权限(Special) :如
SYSTEM_ALERT_WINDOW(悬浮窗)、WRITE_SETTINGS,需引导用户到设置页手动开启
权限组
危险权限按功能分组,同一组内授权一个即全部获得:
CALENDAR--- 日历CAMERA--- 相机CONTACTS--- 通讯录LOCATION--- 位置STORAGE--- 存储(Android 10 后逐步废弃)NOTIFICATION--- 通知(Android 13+ 成为运行时权限)
二、Kotlin 代码实战
声明权限
xml
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
单个权限请求
kotlin
class MainActivity : AppCompatActivity() {
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) openCamera() else showPermissionDeniedDialog()
}
private fun checkCameraPermission() {
when {
ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED -> openCamera()
shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) ->
showRationaleDialog { requestPermissionLauncher.launch(Manifest.permission.CAMERA) }
else -> requestPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
}
多权限批量请求
kotlin
private val multiPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
if (permissions.values.all { it }) {
startFeature()
} else {
val denied = permissions.filter { !it.value }.keys
Snackbar.make(binding.root, "被拒绝: $denied", Snackbar.LENGTH_LONG)
.setAction("去设置") { openAppSettings() }.show()
}
}
封装检查工具
kotlin
object PermissionHelper {
fun hasPermissions(context: Context, vararg permissions: String) =
permissions.all {
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
}
}
三、避坑指南
坑1:忘记处理"不再询问"
用户勾选"不再询问"后,shouldShowRequestPermissionRationale 返回 false。此时应引导跳转系统设置页:
kotlin
private fun openAppSettings() {
startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.parse("package:$packageName")
})
}
坑2:通知权限(Android 13+)
Android 13 将 POST_NOTIFICATIONS 列为运行时权限,默认拒绝。很多 App 在 13 设备上不弹通知就是这个原因。建议在合适的场景(如用户操作触发)请求,而非冷启动直接弹。
坑3:存储权限版本变化
- Android 9 及以下:
READ_EXTERNAL_STORAGE/WRITE_EXTERNAL_STORAGE - Android 10:引入 Scoped Storage,传统存储权限基本失效
- Android 13:细化为
READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO
坑4:registerForActivityResult 注册时机
必须在 onCreate 中(或 onStart 之前)注册,不能在按钮点击回调中懒加载创建,否则抛 IllegalStateException。
四、总结
- 使用
registerForActivityResult替代废弃的onRequestPermissionsResult - 危险权限必须运行时请求,普通权限声明即用
- 处理好"不再询问"场景,引导用户到设置页
- 关注版本差异做兼容
- 请求前给用户上下文说明,降低拒绝率
权限设计本质是隐私保护与功能的平衡------宁可少要权限,也别让用户卸载你的 App。