10、Camera 权限管理:Camera权限声明、运行时权限请求、权限回调处理、权限被拒时的降级策略

说到 Camera 权限,我估计不少同学都踩过坑。明明清单文件里写了权限,一运行还是闪退。或者用户拒绝后,App 直接崩溃。嗯,这些我都经历过。

Camera 权限管理,说白了就是三个事:声明、请求、处理。声明是写在清单里的,请求是运行时弹窗的,处理是用户点完「拒绝」后你要怎么办。今天咱们就把这三件事彻底讲透。

10.1 权限声明:清单文件里的必修课

先看最基础的。Android 里 Camera 相关的权限主要有两个:

权限名 用途 级别
android.permission.CAMERA 访问摄像头硬件 危险权限(需运行时请求)
android.permission.RECORD_AUDIO 录制视频时采集音频 危险权限(需运行时请求)

AndroidManifest.xml 里这样写:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

<!-- 如果你只需要拍照,不需要录音,可以不加 RECORD_AUDIO -->
<!-- 但如果你要录像,两个都得有 -->

这里有个小细节。如果你只是用 Intent 调系统相机拍照,其实不需要 CAMERA 权限。为什么?因为拍照操作是在系统相机 App 里完成的,你的 App 只是接收返回的图片。但如果你直接操作 CameraCamera2 API,那就必须声明。

我的习惯:只要涉及 Camera API,一律声明 CAMERA 权限。哪怕你只是预览,不拍照。因为打开 Camera 硬件本身就需要这个权限。

10.2 运行时权限请求:别让用户一头雾水

Android 6.0 之后,危险权限必须运行时弹窗请求。你不能在清单里写了就完事。用户有权利说「不」。

我个人习惯用 ActivityResultContracts 来处理权限请求。这是官方推荐的方式,代码简洁,不容易出错。

// 在 Activity 或 Fragment 中
private val requestCameraPermission = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
    if (isGranted) {
        // 权限已授予,打开相机
        openCamera()
    } else {
        // 权限被拒绝,执行降级策略
        handlePermissionDenied()
    }
}

// 触发请求
fun checkAndRequestCameraPermission() {
    if (ContextCompat.checkSelfPermission(
            this, Manifest.permission.CAMERA
        ) == PackageManager.PERMISSION_GRANTED
    ) {
        // 已经有权限了,直接打开
        openCamera()
    } else {
        // 需要弹窗请求
        // 这里可以加一个解释说明,告诉用户为什么需要相机权限
        if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
            showRationaleDialog()
        } else {
            requestCameraPermission.launch(Manifest.permission.CAMERA)
        }
    }
}

你想想看,用户第一次拒绝后,第二次再请求时,系统弹窗会多一个「不再询问」的勾选框。如果用户勾了那个,以后就再也不会弹窗了。这时候 shouldShowRequestPermissionRationale 会返回 false。嗯,这里要注意,你不能傻等,得主动引导用户去设置里手动开启。

10.3 权限回调处理:别只处理「同意」

很多新手只处理了权限同意的情况,拒绝的情况直接忽略。结果就是用户拒绝后,相机界面一片黑,或者直接崩溃。

回调处理其实就三种结果:

  • 同意:正常打开相机
  • 拒绝(可再次询问):弹窗解释,再次请求
  • 拒绝(不再询问):引导去设置

我在项目中遇到过这样的情况:用户第一次拒绝后,我们直接弹了一个 Toast 说「权限被拒绝,无法使用相机」。结果用户投诉说「我点错了,想再试一次,但你们不给我机会」。所以后来我改成了这样:

private fun handlePermissionDenied() {
    if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
        // 用户拒绝了,但没有勾选「不再询问」
        // 我们可以再次解释并请求
        showRationaleDialog {
            requestCameraPermission.launch(Manifest.permission.CAMERA)
        }
    } else {
        // 用户勾选了「不再询问」
        // 只能引导去设置
        showSettingsDialog()
    }
}
注意:千万不要在 onRequestPermissionsResult 里直接弹窗请求权限。Android 官方已经不推荐这种方式了。用 ActivityResultContracts 更安全、更清晰。

10.4 权限被拒时的降级策略:别让用户空手而归

用户拒绝权限,你的 App 就罢工?这不太合适。降级策略的核心思想是:没有相机权限,也要让用户能完成核心功能

我总结了几种常见的降级方案:

  • 方案一:使用 Intent 调系统相机 —— 不需要 CAMERA 权限,但用户需要手动拍照再返回
  • 方案二:从相册选择图片 —— 完全不需要相机权限,用户选一张已有的图片
  • 方案三:提供默认图片/占位图 —— 如果功能不强制要求拍照,给个默认图就行
  • 方案四:提示用户手动开启权限 —— 跳转到系统设置页

举个例子,假如你的 App 是「扫码登录」功能:

fun startScan() {
    if (hasCameraPermission()) {
        // 有权限,直接打开扫码界面
        startActivity(Intent(this, ScanActivity::class.java))
    } else {
        // 没有权限,降级方案
        showOptionsDialog(
            "扫码需要相机权限",
            "您可以选择:",
            listOf(
                "1. 授予相机权限(推荐)",
                "2. 手动输入验证码",
                "3. 使用相册中的二维码图片"
            ),
            onGrant = { requestCameraPermission() },
            onManualInput = { startManualInputActivity() },
            onPickFromGallery = { pickImageFromGallery() }
        )
    }
}

你看,用户即使拒绝了权限,依然有两条路可以走。这样用户体验就好多了。

核心原则:权限被拒 ≠ 功能不可用。降级策略的目的是让用户「有路可走」,而不是「无路可退」。

10.5 知识体系总览

下面这张图把 Camera 权限管理的完整流程串起来了。从声明到请求,再到回调处理,最后是降级策略。你可以对照着看,哪个环节还没做到位。

Camera 权限管理完整流程 1. 权限声明 AndroidManifest.xml 2. 运行时请求 ActivityResultContracts 3. 回调处理 onResult(isGranted) ✅ 同意 打开 Camera ⚠️ 拒绝(可再次询问) 弹窗解释 → 再次请求 🚫 拒绝(不再询问) 引导去系统设置 降级策略总汇 Intent 调系统相机 无需 CAMERA 权限 从相册选择图片 完全不需要相机 提供默认占位图 非强制拍照场景 跳转系统设置页 用户手动开启 核心原则:权限被拒 ≠ 功能不可用

10.6 避坑指南

最后分享几个我踩过的坑,希望能帮你省点时间:

  • 坑一:onCreate 里直接请求权限。用户还没看到界面,弹窗就出来了,体验很差。我建议在用户点击「拍照」按钮时再请求。
  • 坑二:只检查 CAMERA 权限,忘了 RECORD_AUDIO。录像时如果没有音频权限,录出来的视频没有声音。用户会以为你的 App 有问题。
  • 坑三:降级策略做得太复杂。我曾经做了一个「手动输入验证码 + 从相册选择 + 默认图片」的三级降级,结果用户反馈说「你们 App 怎么这么多选项,我到底该选哪个?」。后来我简化成:有权限就用相机,没权限就弹一个对话框让用户选「去设置开启」或「从相册选」。简单直接。
  • 坑四:没有处理「权限被撤销」的情况。用户可能在设置里手动关掉相机权限,你的 App 还在后台运行。这时候再打开相机就会崩溃。建议在 onResume 里重新检查权限。
一个小技巧:如果你用 CameraX,它内部已经帮你处理了权限检查。但别完全依赖它,自己再检查一遍更保险。我曾经就遇到过 CameraX 版本升级后权限逻辑变了的情况。

好了,Camera 权限管理就讲到这里。记住三个关键词:声明、请求、降级。把这三个环节都做到位,你的 App 在权限这块就不会出大问题。


公众号:蓝海资料掘金营,微信deep3321