第二章:Android Camera 整体架构:从 App 到 Kernel 的完整数据流、CameraService 与 HAL 的交互、Buffer 管理机制
好,我们直接进入正题。这一章,我会带你走一遍 Camera 数据的完整旅程——从你按下快门,到最终拿到一张照片,中间到底发生了什么。
说实话,我刚接触 Android Camera 时,最头疼的就是搞不清数据到底是怎么流转的。App 调了个接口,怎么就跑到硬件里去了?中间隔了多少层?每一层又在干什么?
今天,我们就把它彻底理清楚。
2.1 从 App 到 Kernel:一条完整的链路
先看整体。Android Camera 的架构,从上到下大致分四层:
- App 层:你写的相机应用,或者系统自带的 Camera2 应用。
- Framework 层:主要是 CameraService,它是整个系统的“调度中心”。
- HAL 层:硬件抽象层,厂商在这里实现具体的硬件驱动接口。
- Kernel 层:真正的硬件驱动,比如 V4L2、ISP 驱动。
数据流的方向,说白了就是一条“请求下去,数据上来”的管道。
举个例子:App 想要一帧预览画面。
- App 调用
CameraCaptureSession.capture()。 - Framework 把这个请求封装成
CaptureRequest,发给 CameraService。 - CameraService 把请求转给 HAL 的
process_capture_request()。 - HAL 驱动硬件(比如传感器、ISP)开始干活。
- 硬件采集到原始数据,通过 Kernel 驱动返回给 HAL。
- HAL 把数据填到预先分配好的 Buffer 里,回调给 CameraService。
- CameraService 再把 Buffer 交给 App 的 Surface。
嗯,这里要注意:每一步都是异步的。不是“你调用我,我马上给你结果”。而是“我发起请求,你准备好了通知我”。
核心要点:整个数据流的核心是“请求-响应”模型。App 发出请求,Framework 和 HAL 协作完成,最终把数据送回给 App。
2.2 CameraService 与 HAL 的交互:谁在指挥谁?
CameraService 和 HAL 之间,是通过 HIDL(Android 8.0 之前是 HAL 接口,之后是 HIDL/AIDL)通信的。说白了,就是定义了一套标准的函数接口,厂商必须实现。
我个人习惯把 CameraService 看作“项目经理”,HAL 是“技术工人”。项目经理不关心你怎么焊电路板,他只关心你能不能按时交付。
关键的交互点有这几个:
- open():CameraService 打开一个 Camera 设备,HAL 返回一个设备句柄。
- configure_streams():配置数据流。比如你要预览 1080p,拍照 4K,HAL 需要分配好对应的 Buffer。
- process_capture_request():下发拍照或预览请求。这是最核心的接口。
- notify() / process_capture_result():HAL 通过这两个回调,告诉 CameraService “我干完了”或者“出错了”。
我在项目中遇到过一个问题:某个厂商的 HAL 在 process_capture_request() 里做了太多耗时操作,导致预览卡顿。后来发现,HAL 应该把耗时操作放到独立线程里,回调才是正确的通知方式。
避坑指南:我曾经因为 HAL 回调不及时,导致 App 端 Surface 一直收不到数据,最后 ANR 了。记住,process_capture_result() 必须尽快调用,哪怕数据还没准备好,也要先返回一个空结果占位。
2.3 Buffer 管理机制:数据到底存在哪?
Buffer 管理,是整个 Camera 架构里最容易出问题的地方。你想想看,每一帧画面都是一块内存,预览 30fps 意味着每秒要处理 30 块内存。谁来分配?谁来回收?怎么避免拷贝?
Android 用的是 ION Buffer(或者叫 DMA-BUF),这是一种可以在不同硬件模块之间共享的内存。比如,ISP 可以直接把数据写到 ION Buffer 里,GPU 可以直接读取它来显示,不需要拷贝。
Buffer 的流转过程大致是这样的:
- 分配:在
configure_streams()时,HAL 会分配好一组 Buffer。每个 Stream(预览流、拍照流)都有自己的 Buffer 池。 - 注册:HAL 把这些 Buffer 注册到 CameraService 的 BufferQueue 里。
- 请求:App 发起 capture 时,CameraService 从 BufferQueue 里取出一个空闲 Buffer,连同请求一起发给 HAL。
- 填充:HAL 把硬件采集到的数据填到这个 Buffer 里。
- 返回:HAL 把填好数据的 Buffer 返回给 CameraService,CameraService 再交给 App。
- 回收:App 用完(比如显示完一帧),Buffer 被放回 BufferQueue,等待下一次使用。
这里有个关键点:Buffer 的所有权在不停转移。App 用的时候,HAL 不能碰。HAL 用的时候,App 不能碰。谁碰谁出问题。
警告:Buffer 泄漏是常见问题。如果 HAL 拿了 Buffer 不还,或者 App 用完不释放,很快内存就会耗尽。我曾经调试过一个案子,预览 5 分钟后卡死,最后发现是 HAL 在某个错误路径下没有调用 process_capture_result(),导致 Buffer 一直被占用。
2.4 一个典型的预览数据流示例
为了让你更直观地理解,我画一个简化的时序图(用文字描述):
App CameraService HAL
| | |
|-- capture() ------------->| |
| |-- process_request() ->|
| | |-- 硬件采集
| | |-- 填充 Buffer
| |<- process_result() ---|
|<- onCaptureCompleted() --| |
| | |
|-- 下一帧请求 ----------->| |
你看,每一帧都是独立的请求-响应周期。30fps 就意味着每秒重复 30 次这个流程。
我个人建议,在调试预览卡顿时,先检查这个周期是否稳定。如果某个请求的响应时间突然变长,那问题大概率出在 HAL 或者硬件上。
2.5 小结
这一章我们讲了三个核心点:
- 数据流:从 App 到 Kernel 的四层架构,请求下去,数据上来。
- CameraService 与 HAL 交互:通过 HIDL 接口,项目经理和技术工人的协作模式。
- Buffer 管理:ION Buffer 的分配、流转、回收,所有权转移是关键。
下一章,我们会深入 HAL 层的具体实现,看看 process_capture_request() 里面到底干了什么。到时候我会拿一个真实的 HAL 代码片段来拆解。
嗯,今天就到这里。有问题随时交流。