实战项目:自定义Camera应用开发
说实话,做Camera开发这么多年,我最大的感触就是——纸上得来终觉浅。你读再多文档,不如亲手写一个能跑起来的Camera应用。这一章,我们就来干点实在的。
我会带你从零开始,封装一套Camera2 API的工具类,实现几个常用滤镜,再聊聊性能调优的那些坑。最后,我会分享一些发布时的注意事项。嗯,这些都是我踩过的坑,你直接拿去用就好。
1. 项目结构设计
我个人习惯把Camera项目分成三层:
- UI层:负责预览、拍照、滤镜切换的界面
- 业务层:封装Camera2 API的核心逻辑
- 渲染层:处理滤镜、特效等图像处理
为什么要分层?说白了,就是为了解耦。我在项目中遇到过好几次,因为UI和Camera逻辑混在一起,改个预览尺寸结果把拍照流程搞崩了。分层之后,各管各的,清爽很多。
核心思路:Camera2 API的封装,重点在于状态机的管理。CameraDevice、CameraCaptureSession、CaptureRequest这三个对象,它们的生命周期必须严格把控。
2. Camera2 API封装实战
先来看一段核心封装代码。我简化了一些异常处理,但保留了最关键的逻辑:
public class Camera2Helper {
private CameraManager cameraManager;
private CameraDevice cameraDevice;
private CameraCaptureSession captureSession;
private CaptureRequest.Builder previewRequestBuilder;
// 打开相机
public void openCamera(Context context, String cameraId,
CameraDevice.StateCallback callback) {
cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
try {
cameraManager.openCamera(cameraId, callback, null);
} catch (CameraAccessException e) {
// 我曾经在这里吃过亏——权限没申请就调用了
e.printStackTrace();
}
}
// 创建预览会话
public void createPreviewSession(Surface surface) {
try {
previewRequestBuilder = cameraDevice.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Arrays.asList(surface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
captureSession = session;
// 开始预览
startPreview();
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
你想想看,这段代码里最容易出问题的地方在哪?
是createCaptureSession的回调。如果Surface被销毁了才回调,那就会崩溃。我建议你在回调里加一个isSurfaceValid的判断。
3. 滤镜实现方案
滤镜的实现,我推荐用OpenGL ES。为什么?因为Camera2的预览数据是YUV格式,直接用CPU处理太慢了。用GPU做颜色矩阵变换,效率能提升好几倍。
来看一个简单的黑白滤镜实现:
// 片段着色器 - 黑白滤镜
private static final String FRAGMENT_SHADER_BLACK_WHITE =
"#version 300 es\n" +
"precision mediump float;\n" +
"in vec2 vTexCoord;\n" +
"out vec4 fragColor;\n" +
"uniform sampler2D sTexture;\n" +
"void main() {\n" +
" vec4 color = texture(sTexture, vTexCoord);\n" +
" float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));\n" +
" fragColor = vec4(vec3(gray), color.a);\n" +
"}\n";
这段代码的核心就是那个dot函数。它把RGB三个通道按人眼敏感度加权求和,得到灰度值。嗯,这里要注意,权重系数是有讲究的,不是随便取的。
小技巧:如果你想要复古风滤镜,可以在灰度基础上加一点棕色调。我试过,效果比纯黑白更讨喜。
4. 性能调优实战
性能调优,说白了就是跟帧率较劲。我总结了几条经验:
- 预览尺寸别贪大:1080P就够了,4K预览对大多数手机来说都是负担
- 减少Surface数量:每个Surface都会增加一次数据拷贝
- 用ImageReader的setOnImageAvailableListener:别在主线程处理图像数据
- 滤镜用GPU:CPU做滤镜,帧率直接掉到15fps以下
我曾经在一个项目里,为了追求画质,用了最大分辨率预览。结果在低端机上,预览画面卡得像幻灯片。后来改成自适应分辨率,问题就解决了。
避坑指南:我曾经在华为某款手机上遇到一个bug——预览画面绿屏。排查了半天,发现是SurfaceView的格式不匹配。Camera2要求Surface格式必须是ImageFormat.YUV_420_888,而SurfaceView默认是RGBA。记得在创建Surface时指定格式。
5. 发布注意事项
发布前,有几件事你必须检查:
| 检查项 | 说明 |
|---|---|
| 权限声明 | CAMERA权限、WRITE_EXTERNAL_STORAGE(Android 10以下) |
| 机型适配 | 不同厂商的Camera HAL实现有差异,建议做兼容性测试 |
| 内存泄漏 | CameraDevice和CaptureSession必须及时释放 |
| 后台限制 | Android 9+对后台Camera访问有限制 |
嗯,这里要特别提一下内存泄漏。CameraDevice是一个重量级对象,如果你在Activity销毁时没有关闭它,那下次打开相机就会失败。我建议在onPause里释放,在onResume里重新打开。
6. 知识体系总览
下面这张图,是我整理的本章知识结构。你可以把它当作一个检查清单,看看自己掌握了哪些:
这张图把整个Camera应用开发分成了四个模块。你从UI层入手,逐步深入到业务层和渲染层,最后再回头做性能调优和发布检查。这个顺序,是我个人觉得最不容易出错的路径。
好了,这一章的内容就到这里。代码和思路都给你了,剩下的就是动手去写。记住,Camera开发没有捷径,多踩坑才能成长。
公众号:蓝海资料掘金营,微信deep3321