首页 / Android 入门教程 / 权限系统

Android 入门教程

权限系统

本教程共 100 篇 · 第 83 篇 · 更新于 2026-07-28 · 约 8 分钟阅读

AndroidAndroid 入门教程权限运行时权限AndroidX隐私

83. 权限系统

本节目标:搞清楚 Android 权限的三种类型和请求流程,学会用 ActivityResultContracts 请求运行时权限、处理拒绝场景,理解 Android 11+ 的单次授权等新机制。

应用要访问用户隐私数据(位置、相机、通讯录)或者操作硬件(麦克风、蓝牙),都得先申请权限。Android 的权限系统从早期的「装了就给」演变成「运行时按需请求」,越来越严。

权限分三类

1. 安装时权限(Install-time)

风险低,装应用时自动授予。比如 INTERNETVIBRATESET_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_CONTACTSWRITE_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)
Warning

targetSdk 升到 33 后,没申请 POST_NOTIFICATIONS 的应用通知一律不显示。老应用升级要补这块。

权限最佳实践

  1. 按需请求:用到时再请求,别一开应用就问一堆。
  2. 解释原因:请求前说明为什么需要,特别是非显然的权限。
  3. 优雅降级:权限被拒不能崩,要有备用方案。
  4. 检查已授权:请求前先检查,已授权就别再问。
  5. 声明最少:能不申请就不申请,能用其他方式实现就别用权限。

用 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()
Tip

Dexter 用着方便,但 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。

常见坑

  1. manifest 没声明就请求:直接拒绝,永远拿不到。
  2. 运行时不检查直接用:崩溃,权限没拿到。
  3. Activity 销毁后请求回调丢失:用 registerForActivityResult 注册(在 onCreate 或者 Composable 顶层),不用回调里直接更新 UI。
  4. 没处理「不再询问」:用户勾了之后请求没反应,体验差。
  5. targetSdk 升 33 没加通知权限:通知不显示。
  6. 后台定位权限:Android 10+ 单独申请,要先有前台权限才能请求后台。

小结

Android 权限分安装时、运行时、特殊三类。运行时权限用 ActivityResultContracts.RequestPermission() 请求,多个用 RequestMultiplePermissions。处理「不再询问」要 shouldShowRequestPermissionRationale + 跳设置。按需请求、解释原因、优雅降级是最佳实践。

下一章讲通知系统,看怎么发通知。