2. Camera权限与配置:AndroidManifest权限声明、运行时权限处理、Camera特性检测

做Android摄像头开发,第一步不是写代码,而是搞定权限。这事儿我踩过不少坑,今天跟你好好聊聊。

说白了,摄像头权限就两类:一个是声明我要用,一个是运行时问用户要。这两步缺一不可,顺序也不能搞错。

2.1 AndroidManifest中的权限声明

先打开你的 AndroidManifest.xml,在 <manifest> 标签里加上这几行:

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

<!-- 如果你还需要录制视频 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />

<!-- 存储权限,Android 10以下需要 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />

嗯,这里要注意。从Android 6.0开始,CAMERA属于危险权限,光声明不够,还得运行时申请。但如果你不声明,运行时申请也不会弹窗——系统直接拒绝。

⚠️ 我曾经遇到过:有个同事只写了运行时申请,忘了在Manifest里声明。结果在小米手机上直接崩溃,在华为上静默失败。排查了半天才发现是声明漏了。

另外,如果你的应用必须依赖摄像头才能工作(比如扫码App),可以加个 uses-feature

<uses-feature android:name="android.hardware.camera" android:required="true" />

这样Google Play会帮你过滤掉没有摄像头的设备。如果你的App没有摄像头也能用大部分功能,设成 false 就行。

2.2 运行时权限处理

声明完了,接下来就是运行时弹窗。我个人习惯把权限申请封装成一个工具类,这样每个页面调用起来都干净。

核心流程其实就三步:

  1. 检查权限:用 ContextCompat.checkSelfPermission()
  2. 申请权限:用 ActivityCompat.requestPermissions()
  3. 处理回调:重写 onRequestPermissionsResult()

看代码:

// 检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
        != PackageManager.PERMISSION_GRANTED) {
    // 没有权限,需要申请
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.CAMERA},
            REQUEST_CAMERA_PERMISSION);
} else {
    // 已有权限,直接打开摄像头
    openCamera();
}

回调处理:

@Override
public void onRequestPermissionsResult(int requestCode,
        @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CAMERA_PERMISSION) {
        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 用户同意了
            openCamera();
        } else {
            // 用户拒绝了
            showPermissionDeniedDialog();
        }
    }
}
💡 小技巧:如果用户点了「拒绝并不再询问」,下次再申请就不会弹窗了。我建议在这种情况下引导用户去设置里手动开启。可以用 shouldShowRequestPermissionRationale() 来判断是否被永久拒绝。

你想想看,用户拒绝一次后,第二次申请时弹个解释说明,成功率会高很多。我在项目里试过,从40%提升到了70%以上。

2.3 Camera特性检测

权限拿到了,但设备不一定支持你想要的特性。比如有些低端机没有前置摄像头,或者不支持你需要的分辨率。

检测摄像头数量:

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
    String[] cameraIdList = manager.getCameraIdList();
    // cameraIdList.length 就是摄像头数量
    for (String id : cameraIdList) {
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
        Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
        if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
            // 这是前置摄像头
        }
    }
} catch (CameraAccessException e) {
    e.printStackTrace();
}

检测是否支持闪光灯:

Boolean hasFlash = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
if (hasFlash != null && hasFlash) {
    // 支持闪光灯
}

检测支持的分辨率:

StreamConfigurationMap configMap =
        characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (configMap != null) {
    Size[] outputSizes = configMap.getOutputSizes(ImageFormat.JPEG);
    // 遍历 outputSizes 找到你需要的分辨率
}

核心要点:不要假设所有设备都一样。我见过一台平板只有后置摄像头,也见过某款手机最大只支持 1920x1080 的预览。提前检测,提前适配,比用户反馈崩溃再修要好得多。

下面这张图总结了整个权限与配置的流程:

Camera 权限与配置流程 ① Manifest 声明 uses-permission CAMERA ② 运行时权限申请 requestPermissions() ③ 特性检测 CameraCharacteristics 用户是否授权? 显示拒绝提示 引导用户开启 检测摄像头数量 分辨率/闪光灯 ✅ 开始 Camera 操作 三步走:声明 → 申请 → 检测,缺一不可

说实话,权限和配置这部分看起来简单,但实际项目中出问题最多的就是这里。我见过太多App因为权限没处理好,在用户手机上直接闪退。

最后给你几个实战建议:

  • 权限申请时机:不要一启动就弹窗。等用户真正要用摄像头时再申请,成功率更高。
  • 拒绝后的处理:不要直接退出App。给用户一个友好的提示,告诉他为什么需要这个权限。
  • 特性检测要缓存:每次打开摄像头都查一遍特性有点浪费。我习惯在Application初始化时查一次,存到全局变量里。
  • 兼容性测试:不同厂商对权限的处理有差异。比如某些国产ROM,即使你申请了权限,用户也可以在设置里单独关闭某个权限。

📌 我的经验:在项目的Application类里做一个统一的权限检测和特性缓存。这样所有页面都能复用,代码也干净。你想想看,如果每个Activity都写一遍检测逻辑,维护起来多痛苦。

好了,权限和配置就聊到这儿。记住这个流程:声明 → 申请 → 检测。每一步都做到位,摄像头这块的基础就打牢了。


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