3. CameraManager与CameraDevice:获取CameraManager、打开CameraDevice、处理Camera状态回调

好,咱们正式开始接触摄像头了。

前面两章我们把CameraX的架构捋了一遍,也知道了Camera2 API才是Android官方推荐的底层接口。从这一章开始,我带你手把手地走一遍Camera2的核心流程。

今天要聊的,是Camera2的“入口”和“大门”——CameraManagerCameraDevice

说白了,CameraManager就是系统里管摄像头的“管家”,而CameraDevice就是具体某个摄像头的“门把手”。你要开门(打开摄像头),得先找到管家,再让他给你钥匙。

3.1 获取CameraManager

CameraManager的获取方式,简单到离谱。

你只需要在Activity或者Service里,通过getSystemService()就能拿到。

CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

嗯,就这一行。

不过我个人习惯把它封装成一个单例,或者放在Application里初始化。为什么呢?因为CameraManager本身是系统服务,不会频繁变化,你每次打开摄像头都去重新获取一次,其实没必要。

小提示:CameraManager是线程安全的,你可以在子线程里调用它的方法。但回调默认跑在Handler所在的线程,这个后面会细说。

3.2 枚举摄像头:getCameraIdList

拿到CameraManager之后,第一件事通常是看看设备上有几个摄像头。

try {
    String[] cameraIdList = cameraManager.getCameraIdList();
    for (String id : cameraIdList) {
        Log.d("Camera", "发现摄像头: " + id);
    }
} catch (CameraAccessException e) {
    e.printStackTrace();
}

这里有个坑,我踩过。

getCameraIdList()会抛出CameraAccessException,这个异常通常发生在摄像头被其他应用占用,或者系统服务异常的时候。你想想看,如果用户正在打电话,你突然去枚举摄像头,很可能就抛异常了。

所以,一定要try-catch,别偷懒。

另外,getCameraIdList()返回的ID列表,顺序不一定是固定的。不同厂商、不同Android版本,返回的顺序可能不一样。如果你需要区分前置和后置,别依赖索引,要用getCameraCharacteristics()去查。

3.3 查询摄像头特性:getCameraCharacteristics

每个摄像头都有自己的“身份证”,就是CameraCharacteristics

CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
    Log.d("Camera", "这是前置摄像头");
}

除了判断前后置,你还能拿到很多有用的信息:

  • 支持的输出尺寸(SCALER_STREAM_CONFIGURATION_MAP
  • 是否支持闪光灯(FLASH_INFO_AVAILABLE
  • 传感器方向(SENSOR_ORIENTATION
  • 支持的分辨率列表

我在项目中遇到过一个问题:某款平板的后置摄像头是竖屏安装的,拍出来的照片默认旋转了90度。后来查了SENSOR_ORIENTATION才发现,这个值是90。所以,千万别硬编码旋转角度,一定要从特性里读。

3.4 打开CameraDevice

拿到CameraManager,选好摄像头ID,接下来就是打开它了。

cameraManager.openCamera(cameraId, stateCallback, backgroundHandler);

三个参数:

  • cameraId:你要打开的摄像头ID
  • stateCallback:状态回调,摄像头打开成功、失败、断开都会通知你
  • backgroundHandler:回调执行的Handler,一般传后台线程的Handler

注意,openCamera()是异步的。它不会立刻返回一个CameraDevice,而是通过回调告诉你结果。

为什么要异步?因为打开摄像头可能涉及硬件初始化、权限校验、资源分配,这些操作如果放在主线程,界面会卡住。你想想看,用户点一下拍照按钮,界面卡了3秒才打开摄像头,这体验能好吗?

3.5 CameraDevice.StateCallback:处理摄像头状态

这个回调是Camera2里最核心的接口之一。你需要实现它,然后交给openCamera()

private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(@NonNull CameraDevice cameraDevice) {
        // 摄像头打开成功
        Log.d("Camera", "摄像头已打开");
        // 保存引用,后续创建CaptureSession
        mCameraDevice = cameraDevice;
    }

    @Override
    public void onDisconnected(@NonNull CameraDevice cameraDevice) {
        // 摄像头断开连接(比如被其他应用抢占)
        Log.d("Camera", "摄像头已断开");
        cameraDevice.close();
        mCameraDevice = null;
    }

    @Override
    public void onError(@NonNull CameraDevice cameraDevice, int error) {
        // 摄像头出错
        Log.e("Camera", "摄像头出错: " + error);
        cameraDevice.close();
        mCameraDevice = null;
    }
};

三个回调方法,分别对应三种状态:

回调方法 触发时机 你应该做什么
onOpened() 摄像头成功打开 保存CameraDevice引用,准备创建CaptureSession
onDisconnected() 摄像头被系统断开 关闭CameraDevice,释放资源
onError() 摄像头发生错误 关闭CameraDevice,提示用户或重试
警告:千万不要在onOpened()里直接做耗时操作。这个回调默认跑在Handler线程,但如果你传了主线程的Handler,那就会卡UI。我建议你专门创建一个后台线程的Handler来处理这些回调。

3.6 核心流程总结

来,我把这一章的核心逻辑画成一张图,方便你理解。

CameraManager 与 CameraDevice 核心流程 获取 CameraManager 枚举摄像头 ID 查询摄像头特性 openCamera() 异步打开 StateCallback onOpened() ✅ onError() ❌ onDisconnected() ⚠️ 回调结果决定下一步

这张图把流程串起来了。你从获取CameraManager开始,一路走到打开摄像头,然后通过回调拿到结果。每一步都环环相扣,缺一不可。

3.7 避坑指南

最后,我把自己踩过的坑总结一下,你遇到了可以少走弯路。

  • 别忘了权限:Android 6.0以上需要动态申请CAMERA权限。如果你没申请就调用openCamera(),会直接抛SecurityException
  • 关闭摄像头要及时:CameraDevice用完后一定要调用close(),否则下次打开会失败。我曾经在测试时忘了关,结果连续打开关闭几次后,摄像头就再也打不开了,只能重启手机。
  • Handler别用主线程:虽然可以传主线程的Handler,但回调里如果有耗时操作,会卡UI。我建议你创建一个后台线程的HandlerThread。
  • onDisconnected和onError都要close:这两个回调都意味着摄像头不可用了,记得释放资源。

核心要点回顾:

  • CameraManager是系统服务,通过getSystemService()获取
  • getCameraIdList()枚举摄像头,用getCameraCharacteristics()查询特性
  • openCamera()是异步操作,结果通过StateCallback返回
  • StateCallback的三个回调:onOpened、onDisconnected、onError,都要妥善处理
  • 用完记得close,否则下次打不开

好了,这一章就到这里。CameraManager和CameraDevice是Camera2的基石,你把它俩搞明白了,后面的CaptureSession和CaptureRequest才能玩得转。


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