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 只是接收返回的图片。但如果你直接操作 Camera 或 Camera2 API,那就必须声明。
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 权限管理的完整流程串起来了。从声明到请求,再到回调处理,最后是降级策略。你可以对照着看,哪个环节还没做到位。
10.6 避坑指南
最后分享几个我踩过的坑,希望能帮你省点时间:
- 坑一:在
onCreate里直接请求权限。用户还没看到界面,弹窗就出来了,体验很差。我建议在用户点击「拍照」按钮时再请求。 - 坑二:只检查
CAMERA权限,忘了RECORD_AUDIO。录像时如果没有音频权限,录出来的视频没有声音。用户会以为你的 App 有问题。 - 坑三:降级策略做得太复杂。我曾经做了一个「手动输入验证码 + 从相册选择 + 默认图片」的三级降级,结果用户反馈说「你们 App 怎么这么多选项,我到底该选哪个?」。后来我简化成:有权限就用相机,没权限就弹一个对话框让用户选「去设置开启」或「从相册选」。简单直接。
- 坑四:没有处理「权限被撤销」的情况。用户可能在设置里手动关掉相机权限,你的 App 还在后台运行。这时候再打开相机就会崩溃。建议在
onResume里重新检查权限。
CameraX,它内部已经帮你处理了权限检查。但别完全依赖它,自己再检查一遍更保险。我曾经就遇到过 CameraX 版本升级后权限逻辑变了的情况。
好了,Camera 权限管理就讲到这里。记住三个关键词:声明、请求、降级。把这三个环节都做到位,你的 App 在权限这块就不会出大问题。
公众号:蓝海资料掘金营,微信deep3321