权限系统
本教程共 100 篇 · 第 83 篇 · 更新于 2026-07-28 · 约 8 分钟阅读
83. 权限系统
本节目标:搞清楚 Android 权限的三种类型和请求流程,学会用
ActivityResultContracts请求运行时权限、处理拒绝场景,理解 Android 11+ 的单次授权等新机制。
应用要访问用户隐私数据(位置、相机、通讯录)或者操作硬件(麦克风、蓝牙),都得先申请权限。Android 的权限系统从早期的「装了就给」演变成「运行时按需请求」,越来越严。
权限分三类
1. 安装时权限(Install-time)
风险低,装应用时自动授予。比如 INTERNET、VIBRATE、SET_WALLPAPER。
声明在 manifest 就行:
<uses-permission android:name="android.permission.INTERNET" />
用户在应用商店能看到这些权限,但不用单独同意。
2. 运行时权限(Runtime / Dangerous)
涉及隐私或敏感操作,运行时弹窗让用户确认。比如:
ACCESS_FINE_LOCATION:精确定位CAMERA:相机RECORD_AUDIO:录音READ_CONTACTS:读通讯录POST_NOTIFICATIONS:发通知(Android 13+)
这类权限必须在 manifest 声明 + 运行时请求,缺一不可。
3. 特殊权限(Special)
功能特别强大,不能在普通弹窗里申请,要跳到系统设置让用户手动开。比如:
SYSTEM_ALERT_WINDOW:悬浮窗WRITE_SETTINGS:修改系统设置REQUEST_INSTALL_PACKAGES:安装其他应用
用 Settings.ACTION_MANAGE_* Intent 跳到设置页:
val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName"))
startActivity(intent)
权限组
相关权限会归到一个组里。比如 READ_CONTACTS 和 WRITE_CONTACTS 同属「通讯录」组。
授权某个权限时,同组的其他权限默认也授权。但别依赖这个行为—分组规则可能变,每个权限该请求还是要请求。
请求运行时权限
第一步:manifest 声明
<uses-permission android:name="android.permission.CAMERA" />
第二步:运行时请求
现代写法用 ActivityResultContracts,比老的 requestPermissions 简洁:
class CameraActivity : ComponentActivity() {
private val requestCameraLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) {
openCamera()
} else {
showExplanation()
}
}
fun requestCamera() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED) {
openCamera()
} else {
requestCameraLauncher.launch(Manifest.permission.CAMERA)
}
}
}
在 Compose 里请求
Compose 用 rememberLauncherForActivityResult:
@Composable
fun CameraScreen() {
val context = LocalContext.current
var hasPermission by remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
hasPermission = granted
}
LaunchedEffect(Unit) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
launcher.launch(Manifest.permission.CAMERA)
} else {
hasPermission = true
}
}
if (hasPermission) {
CameraPreview()
} else {
Text("需要相机权限")
}
}
一次请求多个权限
RequestMultiplePermissions 一次请求多个:
private val launcher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { result ->
val cameraGranted = result[Manifest.permission.CAMERA] == true
val audioGranted = result[Manifest.permission.RECORD_AUDIO] == true
if (cameraGranted && audioGranted) {
startRecording()
}
}
fun requestAll() {
launcher.launch(arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO
))
}
处理「不再询问」
用户拒绝权限时可以勾「不再询问」。之后再请求权限,系统直接返回拒绝,不弹窗。
检测这种状态:
fun shouldShowRationale(activity: Activity, permission: String): Boolean {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
}
shouldShowRequestPermissionRationale 返回 true 表示用户之前拒绝过但没勾「不再询问」,可以显示解释。
完整流程:
fun requestWithRationale(permission: String) {
when {
// 已授权
ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -> {
doAction()
}
// 用户拒绝过但没勾「不再询问」,显示解释
shouldShowRequestPermissionRationale(this, permission) -> {
AlertDialog.Builder(this)
.setTitle("需要权限")
.setMessage("这个权限用来...")
.setPositiveButton("去授权") { _, _ ->
launcher.launch(permission)
}
.show()
}
// 第一次请求,或者用户勾了「不再询问」
else -> {
launcher.launch(permission)
}
}
}
Note第一次请求时
shouldShowRequestPermissionRationale返回 false(因为还没拒绝过)。用户拒绝过一次但没勾「不再询问」返回 true。用户勾了「不再询问」返回 false。要区分第一次和「不再询问」,自己记个 SharedPreferences 标记。
跳到应用设置
用户勾了「不再询问」后,再请求没用。只能引导用户去设置里手动开:
fun openAppSettings() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
}
startActivity(intent)
}
弹个对话框告诉用户「权限被拒绝,请到设置开启」,给个跳转按钮。
Android 11+ 单次授权
Android 11(API 30)起,权限弹窗多了「仅此一次」选项。用户选这个,应用这次能用,进程重启后又要重新请求。
作为开发者不用改代码,系统自动处理。但要知道这种行为,测试时遇到「上次给了权限这次又问」是正常的。
Android 13+ 通知权限
Android 13(API 33)起,发通知要申请 POST_NOTIFICATIONS 运行时权限:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
if (granted) {
sendNotification()
}
}
// 在合适时机请求
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
WarningtargetSdk 升到 33 后,没申请
POST_NOTIFICATIONS的应用通知一律不显示。老应用升级要补这块。
权限最佳实践
- 按需请求:用到时再请求,别一开应用就问一堆。
- 解释原因:请求前说明为什么需要,特别是非显然的权限。
- 优雅降级:权限被拒不能崩,要有备用方案。
- 检查已授权:请求前先检查,已授权就别再问。
- 声明最少:能不申请就不申请,能用其他方式实现就别用权限。
用 Dexter 简化
第三方库 Dexter 把权限请求包装得更简洁:
Dexter.withContext(this)
.withPermission(Manifest.permission.CAMERA)
.withListener(object : PermissionListener {
override fun onPermissionGranted(response: PermissionGrantedResponse) { }
override fun onPermissionDenied(response: PermissionDeniedResponse) { }
override fun onPermissionRationaleShouldBeShown(permission: PermissionToken?, token: PermissionToken) {
token.continuePermissionRequest()
}
})
.check()
TipDexter 用着方便,但
ActivityResultContracts是官方方案,新项目优先用官方。Dexter 适合老项目快速迁移。
Accompanist Permissions(Compose)
Compose 项目可以用 Accompanist Permissions 库:
val permissionState = rememberPermissionState(Manifest.permission.CAMERA)
Button(onClick = { permissionState.launchPermissionRequest() }) {
Text("请求相机权限")
}
if (permissionState.status.isGranted) {
CameraPreview()
}
Note新版 Compose 已经把权限 API 合并到
androidx.activity:activity-compose,Accompanist Permissions 处于维护模式。优先用官方 API。
常见坑
- manifest 没声明就请求:直接拒绝,永远拿不到。
- 运行时不检查直接用:崩溃,权限没拿到。
- Activity 销毁后请求回调丢失:用
registerForActivityResult注册(在 onCreate 或者 Composable 顶层),不用回调里直接更新 UI。 - 没处理「不再询问」:用户勾了之后请求没反应,体验差。
- targetSdk 升 33 没加通知权限:通知不显示。
- 后台定位权限:Android 10+ 单独申请,要先有前台权限才能请求后台。
小结
Android 权限分安装时、运行时、特殊三类。运行时权限用 ActivityResultContracts.RequestPermission() 请求,多个用 RequestMultiplePermissions。处理「不再询问」要 shouldShowRequestPermissionRationale + 跳设置。按需请求、解释原因、优雅降级是最佳实践。
下一章讲通知系统,看怎么发通知。