3. CameraDevice.open() 深度解析:open流程、权限检查、设备状态机、异常处理

CameraDevice.open() 是 Camera2 框架中应用层与相机硬件建立连接的第一道关口。这个调用看似简单,背后却涉及权限验证、状态机跃迁、HAL 层初始化以及多级异常处理。理解 open 的完整流程,是后续 Pipeline 调优的基础。

3.1 open() 调用流程全景

从应用层调用 CameraManager.openCamera() 到 CameraDevice 实例就绪,整体流程分为四个阶段:

  1. 权限预检:检查应用是否持有 CAMERA 权限,以及设备是否被其他进程占用。
  2. 状态机跃迁:CameraService 将设备状态从 UNOPENED 切换至 OPENING,并锁定设备。
  3. HAL 初始化:通过 Camera HAL 接口调用 open(),完成硬件资源分配与流模板初始化。
  4. 回调通知:通过 CameraDevice.StateCallback 返回 onOpened() 或 onError()。
关键指标: 在 Android 原生实现中,open() 的阻塞时间通常控制在 200ms 以内;超过 500ms 会触发 ANR 风险。HAL 层 open 耗时是主要瓶颈。
CameraDevice.open() 调用流程 App: openCamera() 权限预检 状态机跃迁 HAL open() onOpened() onError() 权限检查失败 → SecurityException 设备被占用 → CameraAccessException

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 的跃迁。状态定义如下:

CameraDevice 状态机(open 相关) UNOPENED OPENING OPENED CLOSED open() HAL 初始化成功 close() open 失败
调优要点: 状态机跃迁过程中,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() 的耗时分布通常如下:

open() 耗时分布(典型值) 权限检查 ~5ms 状态机 ~2ms HAL open() + 硬件初始化 ~80-200ms 回调 ~1ms HAL open 占整体耗时 80% 以上,是优化重点 建议:HAL 层做延迟初始化,将 sensor 启动推迟到 createCaptureSession
优化建议:
  • HAL 层:将 sensor 上电、ISP 初始化等耗时操作从 open() 移至 configureStreams() 阶段。
  • 应用层:在 Activity onCreate() 中提前调用 CameraManager.awaitCameraService() 预热服务。
  • 系统层:CameraService 使用 Binder 线程池,避免 open() 阻塞 UI 线程。

3.6 实战:open 失败排查清单

当 open() 返回 onError() 时,按以下清单快速定位:

  1. 检查 logcat:搜索 CameraService、CameraDeviceClient 关键字,定位错误码。
  2. 验证 cameraId:确保 cameraId 来自 CameraManager.getCameraIdList(),而非硬编码。
  3. 确认权限:运行时权限是否已授予?设备管理员是否禁用了相机?
  4. 检查并发:是否有其他进程(如系统相机)占用了同一设备?
  5. HAL 层 dump:通过 dumpsys media.camera 查看 HAL 状态,确认是否卡在初始化阶段。
调试命令: adb shell dumpsys media.camera 可输出所有 CameraDevice 的状态、客户端列表以及 HAL 版本信息,是排查 open 问题的第一利器。