与旧的Camera API相比,L中引入的Camera2 API要复杂得多:涉及十多个类,通过callback进行异步操作,同时增加了许多令人困惑的捕获控制(capture controls)和元数据(meta data)。

面对一个复杂的系统时,通常用UML类图来进行一下概览。以下是Camera2 API的类图。 Android Camera2 API 架构_类图

按照上图中的步骤编号,我们一一对应起来分析:

1.​​CameraManager​​​这个类, 我们可以通过它来获取系统中所有可用的camer设备,每个camera都有一个指定的​​cameraId​​​来标识。通过​​cameraId​​​,我们可以获得指定camera设备的属性, 这些属性由​​CameraCharacteristics​​类存储,其中包括了“前置或后置摄像头”,“支持的输出分辨率”等属性。

2.对于camera捕获的图像数据,应该有一个最终的接收者来处理:比如使用​​SurfaceView​​​或​​SurfaceTexture​​​进行预览处理、使用​​ImageReader​​​进行静态图片处理、或者使用​​MediaRecoder​​​进行视频录制处理,这些处理操作都会有一个接收者——​​Surface​​​。也就是说,对于这些操作来说,我们需要想办法获取到一个​​Surface​​​,然后设置到​​CamerDevice​​​中,才可以接收到camera捕获的图像数据。 在下面的步骤中会讲述如何创建​​CaptureSession​​​列表、如何将​​Surface​​​添加到​​CaptureRequest​​​中,届时将会明白​​Surface​​的作用。

3.通过调用​​CameraManager.open(cameraId,callback)​​​来获取​​CameraDevice​​​。 由于调用是异步的,因此需要在​​onOpened()​​​回调中获取​​CameraDevice​​。

4.从​​CameraDevice​​​创建​​CaptureRequest​​​,​​CaptureRequest​​​实际上是一个固定的数据包(immutable package),我们从camera获取单张图像后,通过​​CaptureRequest​​​对其设置和输出。这里应用了Builder模式,如上面的类图所示,​​CaptureReques.Builder​​​是从​​CameraDevice​​​中的​​createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)​​​方法来创建的。 然后,我们使用构建器来设置变其他属性,比如​​addTarget(Surface)​​​来设置​​CaptureRequest​​​的接收者。注意,我们通常不直接创建​​CaptureRequest​​,因为这样比较容易出错。

5.从​​CameraDevice​​​创建​​CaptureRequestSession​​​, ​​CaptureRequestSession​​​用于提交上面创建好的​​CaptureRequest​​​。要初始化​​CaptureRequestSession​​​,必须提供初始化好的​​Surface​​​,这在步骤2中已经讨论过。另请注意,​​CaptureRequestSession​​的创建也是异步的。

6.通过​​CaptureRequestSession​​​发送​​CaptureRequest​​​,有几个API可以做到这一点:​​capture()​​​用于发出一次性拍照命令,​​setRepeatingRequest()​​​用于发出显示预览的重复请求。此外,在发送​​CaptureRequest​​请求之前,需要设置自动对焦模式、白平衡模式等内容。

7.获取捕获的图像结果。捕获到的结果将通过​​CameraCaptureSession.CaptureCallback​​​异步返回,你可以在这个回调中播放拍照声音(例如“咔嚓”的声音),也可以在​​onCaptureProcessed​​​方法里接收一张张的​​CaptureResult​​。

8.对于步骤7来说,我们也可以在​​onCaptureComplete​​​方法的​​TotalCaptureResult​​中一次性获取所有数据。

最后总结一下,camera捕获到的图像数据输出到surface处理,正如步骤2中所讨论的。camera是生产者,surface是消费者。camera保持对图像数据进行排队,一旦数据可用,将通知消费者surface处理它们:TextureView会将图片数据转换为纹理,并在绘制到屏幕;MediaRecoder会将图像传递给编解码器进行处理;对于ImageReader,由我们自己处理数据。