1. Camera与Camera2 API概览:Android摄像头架构演进

做Android摄像头开发这么多年,我最大的感受就是——这玩意儿真不是随便调个接口就能搞定的。尤其是从Camera API切换到Camera2 API那段时间,说实话,很多老项目直接翻车了。

今天咱们就来聊聊Android摄像头架构的演进,以及Camera和Camera2这两套API到底有什么区别。嗯,我会结合我实际踩过的坑来讲,希望能帮你少走弯路。

1.1 Android摄像头架构演进

Android的摄像头架构,说白了经历了三个阶段:

  • Android 1.0 ~ 4.4:Camera API(android.hardware.Camera)
  • Android 5.0 ~ 7.0:Camera2 API预览版 + 正式版(android.hardware.camera2)
  • Android 8.0+:Camera2 API成为主流,Camera API被标记为废弃

为什么会这样?因为早期的Camera API设计太简单了。你想想看,那时候手机摄像头就一个后置,分辨率也不高,调个预览、拍个照就完事了。但后来手机摄像头越来越多——双摄、三摄、景深、广角、长焦……老API根本撑不住。

我记得在2015年做第一个双摄项目时,用Camera API去控制两个摄像头,那叫一个痛苦。两个Camera对象互相抢资源,崩溃率居高不下。后来Android 5.0推出了Camera2 API,我才算真正解脱。

核心变化:Camera API是同步阻塞模型,Camera2 API是异步管道模型。说白了,Camera2把摄像头当成一个流水线,你可以精细控制每一道工序。

1.0 Camera API 同步阻塞 5.0 Camera2 API 异步管道 8.0+ Camera2 主流 废弃Camera API Android版本演进 →

1.2 Camera API vs Camera2 API 对比

很多新手问我:到底用哪个?我的建议是:新项目一律用Camera2。除非你要兼容Android 4.4以下的古董机,否则别碰老API了。

咱们直接看对比表,一目了然:

对比项 Camera API Camera2 API
引入版本 Android 1.0 Android 5.0
模型 同步阻塞 异步管道
摄像头控制 单一Camera对象 CameraManager → CameraDevice → CaptureSession
多摄像头支持 不支持(需hack) 原生支持
手动控制 有限(白平衡、曝光补偿) 全面(ISO、快门、对焦、AE/AWB等)
预览/拍照 setPreviewCallback CaptureRequest + CaptureCallback
性能 较低,容易ANR 高,支持多路并发
当前状态 废弃(deprecated) 推荐使用

我的经验:曾经有个项目为了快速上线用了Camera API,结果后来要加美颜滤镜,发现预览帧回调延迟高得离谱。换成Camera2后,用SurfaceView + ImageReader,延迟直接降了60%。所以,别图一时省事。

1.3 核心类介绍

Camera2 API的核心类就三个:CameraManagerCameraDeviceCameraCaptureSession。它们的关系就像工厂流水线:

  • CameraManager:工厂管理员,负责发现摄像头、打开摄像头
  • CameraDevice:具体的摄像头设备,相当于一台机器
  • CameraCaptureSession:生产流水线,负责处理预览、拍照等任务

1.3.1 CameraManager

这是入口。你首先要通过getSystemService()拿到它:

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

然后用它来:

  • 获取摄像头列表:manager.getCameraIdList()
  • 查询摄像头特性:manager.getCameraCharacteristics(cameraId)
  • 打开摄像头:manager.openCamera(cameraId, callback, handler)

这里有个坑——记得检查摄像头权限。我遇到过好几次,在Android 6.0以上没动态申请权限,直接调openCamera,结果回调里报CameraAccessException。嗯,这个异常就是权限问题的典型信号。

1.3.2 CameraDevice

当你成功打开摄像头后,会得到一个CameraDevice对象。它代表了一个物理摄像头。你可以用它来:

  • 创建CaptureRequest:定义拍照或预览的参数
  • 创建CameraCaptureSession:建立数据通道
  • 关闭摄像头:device.close()

我个人习惯在onOpened回调里立即创建预览请求,而不是等用户点击按钮。因为摄像头打开本身就有延迟,早点创建请求能缩短用户等待时间。

CameraDevice.StateCallback callback = new CameraDevice.StateCallback() {
    @Override
    public void onOpened(@NonNull CameraDevice camera) {
        // 摄像头已打开,可以创建CaptureSession了
        createCaptureSession(camera);
    }

    @Override
    public void onDisconnected(@NonNull CameraDevice camera) {
        camera.close();
    }

    @Override
    public void onError(@NonNull CameraDevice camera, int error) {
        camera.close();
        // 处理错误,比如摄像头被占用
    }
};

注意:CameraDevice的close()方法必须在主线程调用,否则会抛出异常。我曾经在子线程里调了close(),结果崩溃日志查了半天才发现是线程问题。

1.3.3 CameraCaptureSession

这是最核心的类。它负责管理摄像头数据的流向。你可以把它理解成一条流水线:

  • 输入:CaptureRequest(告诉摄像头要做什么)
  • 输出:CaptureCallback(接收摄像头返回的数据)
  • 目标:Surface(数据显示到哪里,比如TextureView、ImageReader)

创建Session的典型代码:

cameraDevice.createCaptureSession(
    Arrays.asList(previewSurface, imageReaderSurface),
    new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {
            // Session已就绪,可以发送请求了
            startPreview(session);
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
            // 配置失败,通常是Surface不合法
        }
    },
    handler
);

这里有个关键点:Surface的顺序很重要。第一个Surface通常是预览用的,第二个是拍照用的。如果你把顺序搞反了,预览画面可能出不来。我刚开始学Camera2时就犯过这个错,预览黑屏了半天。

1.4 核心流程总结

咱们用一张图来总结Camera2的工作流程:

CameraManager 获取摄像头列表 CameraDevice 打开摄像头 CaptureSession 创建会话 请求 CaptureRequest 详细流程 1. getSystemService(CAMERA_SERVICE) → CameraManager 2. manager.getCameraIdList() → 选择摄像头ID 3. manager.openCamera(id, callback, handler) → CameraDevice 4. device.createCaptureSession(surfaces, callback, handler) → CaptureSession 5. session.setRepeatingRequest(request, callback, handler) → 开始预览 注意:所有回调都在CameraHandler线程中执行,不要做耗时操作

1.5 避坑指南

最后,分享几个我实际项目中踩过的坑:

  • 摄像头被占用:曾经有个bug,用户打开相机后切到后台再回来,摄像头就打不开了。原因是onPause里没调close()。记住:CameraDevice和CaptureSession都要在onPause中释放
  • SurfaceView vs TextureView:我建议用TextureView,因为它支持硬件加速,而且可以方便地做旋转、缩放。SurfaceView虽然性能更好,但没法做动画变换。
  • 预览方向:默认摄像头预览是横屏的,竖屏时画面会旋转90度。记得用CameraCharacteristics.SENSOR_ORIENTATION来校正方向。

小技巧:调试Camera2时,可以用adb shell dumpsys media.camera来查看摄像头状态。这个命令能告诉你摄像头是否被占用、当前分辨率、帧率等信息。排查问题非常有用。

好了,这一章的内容就到这里。Camera2 API虽然学习曲线陡峭,但掌握了核心类的用法后,你会发现它其实很灵活。下一章咱们会深入讲解CameraManager的详细用法,包括如何获取摄像头特性、如何选择最佳摄像头等。


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