3. CameraDevice.open() 深度解析:open流程、权限检查、设备状态机、异常处理
CameraDevice.open() 是 Camera2 框架中应用层与相机硬件建立连接的第一道关口。这个调用看似简单,背后却涉及权限验证、状态机跃迁、HAL 层初始化以及多级异常处理。理解 open 的完整流程,是后续 Pipeline 调优的基础。
3.1 open() 调用流程全景
从应用层调用 CameraManager.openCamera() 到 CameraDevice 实例就绪,整体流程分为四个阶段:
- 权限预检:检查应用是否持有
CAMERA权限,以及设备是否被其他进程占用。 - 状态机跃迁:CameraService 将设备状态从
UNOPENED切换至OPENING,并锁定设备。 - HAL 初始化:通过 Camera HAL 接口调用
open(),完成硬件资源分配与流模板初始化。 - 回调通知:通过
CameraDevice.StateCallback返回onOpened()或onError()。
关键指标: 在 Android 原生实现中,open() 的阻塞时间通常控制在 200ms 以内;超过 500ms 会触发 ANR 风险。HAL 层 open 耗时是主要瓶颈。
3.2 权限检查机制
权限检查发生在 CameraService 端,分为三个层级:
| 检查层级 | 检查内容 | 失败结果 |
|---|---|---|
| Manifest 权限 | 应用是否声明 android.permission.CAMERA |
SecurityException |
| 运行时权限 | 用户是否授予相机权限(Android 6.0+) | SecurityException |
| 设备独占锁 | 设备是否已被其他 CameraDevice 打开 | CameraAccessException (CAMERA_IN_USE) |
常见陷阱: 部分设备支持同时打开多个 CameraDevice(如前后摄),但同一物理 cameraId 只能被一个客户端独占。调用 open() 前务必通过
CameraManager.getCameraCharacteristics() 确认设备可用性。
3.3 设备状态机跃迁
CameraDevice 内部维护一个严格的状态机,open() 操作触发从 UNOPENED 到 OPENING 再到 OPENED 的跃迁。状态定义如下:
调优要点: 状态机跃迁过程中,CameraService 会持有设备锁。如果 HAL 层 open() 耗时过长(如 > 1s),会导致其他应用无法打开相机。建议在 HAL 实现中做异步初始化,将耗时操作推迟到第一个流配置时执行。
3.4 异常处理与错误码
open() 调用可能抛出以下异常,应用层必须妥善处理:
// 标准异常处理模板
try {
cameraManager.openCamera(cameraId, stateCallback, backgroundHandler);
} catch (CameraAccessException e) {
switch (e.getReason()) {
case CameraAccessException.CAMERA_IN_USE:
// 设备被其他应用占用,建议等待后重试
break;
case CameraAccessException.MAX_CAMERAS_IN_USE:
// 系统相机数量已达上限
break;
case CameraAccessException.CAMERA_DISABLED:
// 设备被策略禁用(如设备管理员限制)
break;
case CameraAccessException.CAMERA_DISCONNECTED:
// 设备已断开(如 USB 相机拔出)
break;
}
} catch (SecurityException e) {
// 权限不足,引导用户授权
}
| 异常类型 | 错误码 | 触发场景 | 推荐处理 |
|---|---|---|---|
CameraAccessException |
CAMERA_IN_USE |
设备已被其他进程打开 | 等待 1-2s 后重试 |
CameraAccessException |
MAX_CAMERAS_IN_USE |
系统总相机数超限(通常为 2-3 个) | 释放其他 CameraDevice |
CameraAccessException |
CAMERA_DISABLED |
设备策略禁用或硬件故障 | 提示用户检查设置 |
SecurityException |
— | 缺少 CAMERA 权限 | 引导用户授权 |
IllegalArgumentException |
— | cameraId 无效 | 检查 CameraManager 可用 ID 列表 |
3.5 性能调优:open 延迟分析
从性能角度,open() 的耗时分布通常如下:
优化建议:
- HAL 层:将 sensor 上电、ISP 初始化等耗时操作从 open() 移至 configureStreams() 阶段。
- 应用层:在 Activity onCreate() 中提前调用
CameraManager.awaitCameraService()预热服务。 - 系统层:CameraService 使用 Binder 线程池,避免 open() 阻塞 UI 线程。
3.6 实战:open 失败排查清单
当 open() 返回 onError() 时,按以下清单快速定位:
- 检查 logcat:搜索
CameraService、CameraDeviceClient关键字,定位错误码。 - 验证 cameraId:确保 cameraId 来自
CameraManager.getCameraIdList(),而非硬编码。 - 确认权限:运行时权限是否已授予?设备管理员是否禁用了相机?
- 检查并发:是否有其他进程(如系统相机)占用了同一设备?
- HAL 层 dump:通过
dumpsys media.camera查看 HAL 状态,确认是否卡在初始化阶段。
调试命令:
adb shell dumpsys media.camera 可输出所有 CameraDevice 的状态、客户端列表以及 HAL 版本信息,是排查 open 问题的第一利器。