8、Android Camera Framework层:CameraService启动与架构
各位同学,今天我们来啃一块硬骨头——Camera Framework层。说实话,这部分内容我第一次接触时也头大,代码调用链长得像蜘蛛网。但别怕,我带你一层层剥开它。
Framework层说白了就是连接App和HAL的桥梁。你想想看,App调用一个openCamera,中间要经过多少环节?我数过,至少七八个关键节点。今天我们就从CameraService的启动开始讲起。
8.1 CameraService启动流程
CameraService是系统服务,它在Android启动时由SystemServer拉起。我习惯把它的启动分成三个阶段:
- 注册服务:向ServiceManager注册CameraService
- 初始化Provider:加载CameraProviderManager
- 等待客户端:监听来自App的请求
代码层面,启动入口在frameworks/av/services/camera/libcameraservice/CameraService.cpp。核心函数是onFirstRef():
void CameraService::onFirstRef() {
ALOGI("CameraService started");
// 1. 初始化CameraProviderManager
mCameraProviderManager = new CameraProviderManager();
mCameraProviderManager->initialize(this);
// 2. 枚举所有Camera设备
status_t res = enumerateProviders();
if (res != OK) {
ALOGE("Failed to enumerate providers");
}
// 3. 注册服务
publish();
}
这里有个坑,我曾经遇到过:如果HAL层某个Provider初始化超时,整个CameraService启动就会卡住。嗯,后来我们在项目中加了超时保护,才解决这个问题。
8.2 CameraProviderManager架构
CameraProviderManager是CameraService的"眼睛",它负责发现和管理所有Camera HAL Provider。说白了,它就是个中介,帮CameraService找到可用的Camera设备。
它的核心职责有三点:
- 发现Provider:通过HIDL服务发现所有Camera HAL实现
- 管理设备:维护每个Camera设备的生命周期
- 权限控制:检查App是否有权限访问Camera
关键点:CameraProviderManager内部维护了一个mProviders列表,每个Provider对应一个HAL实现。比如高通平台可能有QtiCameraProvider,MTK平台有MtkCameraProvider。
我建议你重点关注CameraProviderManager::initialize()这个函数,它里面调用了HIDL的ICameraProvider::getCameraIdList()来获取设备列表。这个调用是跨进程的,所以会有一定的延迟。
8.3 CameraDeviceClient与CameraDeviceImpl
当App通过CameraManager打开一个Camera设备时,Framework会创建两个关键对象:
| 对象 | 所在进程 | 职责 |
|---|---|---|
| CameraDeviceClient | CameraService进程 | 作为服务端,处理来自App的请求 |
| CameraDeviceImpl | App进程 | 作为客户端,封装对Camera的操作 |
这两个对象通过Binder通信。我记得有一次调试时,发现App发送的CaptureRequest总是丢失,查了半天才发现是Binder缓冲区满了。后来我们调整了Binder线程池大小才解决。
来看一下CameraDeviceClient的创建流程:
// CameraService中
sp<CameraDeviceClient> client = new CameraDeviceClient(
this, // CameraService
cameraId, // 设备ID
clientPackage, // App包名
clientUid, // App UID
remoteCallback // App的回调接口
);
// 初始化
status_t err = client->initialize(mCameraProviderManager);
if (err != OK) {
// 处理错误
}
避坑指南:我曾经遇到一个案例,App在onResume中频繁打开关闭Camera,导致CameraDeviceClient创建失败。原因是上一个client还没完全销毁。解决方案是在关闭Camera时加入同步等待。
8.4 CaptureRequest与CaptureResult的Framework流转
这是Camera Framework最核心的数据流。我画了一张图,帮你理解整个流转过程:
你看这个图,Request是向下流的,Result是向上流的。我刚开始做Camera开发时,总搞混这两个方向。后来记住一句话:"请求往下走,结果往上走",再也没错过。
8.5 Camera2 API详解
Camera2 API是Android 5.0引入的,相比Camera1,它更灵活但也更复杂。核心有三个类:
- CameraManager:管理Camera设备,负责枚举和打开
- CameraDevice:代表一个Camera设备,负责创建CaptureSession
- CaptureSession:管理拍照会话,负责提交CaptureRequest
典型的调用流程是这样的:
// 1. 获取CameraManager
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
// 2. 打开Camera设备
manager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice device) {
// 3. 创建CaptureSession
device.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
// 4. 创建CaptureRequest
CaptureRequest request = device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
request.addTarget(surface);
// 5. 提交请求
session.setRepeatingRequest(request, new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(CaptureRequest request, TotalCaptureResult result) {
// 6. 处理结果
}
}, backgroundHandler);
}
}, backgroundHandler);
}
}, backgroundHandler);
注意:Camera2 API的所有回调都在Binder线程中执行。如果你在回调中做耗时操作,会阻塞整个CameraService。我建议你使用独立的HandlerThread来处理回调。
8.6 Framework与HAL的Binder通信机制
Framework和HAL之间的通信,在Android 8.0之后主要走HIDL。说白了,HIDL就是Android为硬件抽象层设计的Binder通信协议。
通信链路是这样的:
- App通过Binder调用CameraService
- CameraService通过HIDL调用Camera HAL
- HAL处理完后,通过HIDL回调返回结果
- CameraService再通过Binder回调给App
这里有个性能问题,我实际项目中遇到过:每次拍照都要经过两次Binder调用,延迟大概在5-10ms。对于高速连拍场景,这个延迟不可接受。解决方案是使用HIDL的passthrough模式,让CameraService直接加载HAL的so库,减少一次跨进程通信。
来看一下HIDL接口的定义:
// ICameraDevice.hal
interface ICameraDevice {
// 提交CaptureRequest
submitRequest(CaptureRequest request)
generates (Status status, int64_t frameNumber);
// 取消请求
cancelRequest(int64_t frameNumber)
generates (Status status);
// 关闭设备
close() generates (Status status);
};
嗯,这里要注意,HIDL接口的返回值是异步的。你调用submitRequest后,HAL会通过回调函数返回CaptureResult。这个回调也是通过Binder传递的,所以同样有线程安全问题。
我的经验:调试Binder通信问题时,可以用adb shell dumpsys binder查看Binder线程池状态。如果发现线程池满了,多半是某个回调卡住了。我曾经靠这个命令定位过一个顽固的ANR问题。
好了,Framework层的内容就讲到这里。这部分知识比较抽象,我建议你结合源码来理解。特别是CameraDeviceClient.cpp和CameraDeviceImpl.java这两个文件,值得反复阅读。
公众号:蓝海资料掘金营,微信deep3321