25. Camera与SurfaceView:SurfaceView与TextureView对比、SurfaceView在Camera中的应用、性能考量
说到Android摄像头采集,SurfaceView和TextureView这两个家伙是绕不开的。我刚开始做Camera开发时,也在这两者之间纠结过很久。说白了,它们都是用来显示图像数据的,但底层机制和适用场景差别挺大。今天咱们就把它们掰开揉碎了聊一聊。
SurfaceView与TextureView:本质差异
先问一个问题:为什么Android要搞出两个View来显示摄像头画面?
答案其实很简单——性能与灵活性的取舍。
SurfaceView的工作原理,是在WindowManager那里单独开一个图层。这个图层独立于View层级,有自己的Surface。你想想看,它不需要和普通View共享绘制流程,所以绘制效率极高。但代价是什么?它不能做动画、不能做平移缩放,连透明效果都很难搞。
TextureView就不一样了。它本质上是一个普通的View,内部持有一个Texture。它把图像数据当作纹理来处理,所以可以像操作普通View一样做各种变换。我在项目中遇到过需要给摄像头画面加圆角、做缩放动画的场景,TextureView就是首选。
来,看个对比表格会更清楚:
| 特性 | SurfaceView | TextureView |
|---|---|---|
| 图层层级 | 独立图层(WindowManager) | View层级内 |
| 绘制性能 | 高(直接操作Surface) | 中等(需经过纹理合成) |
| 动画/变换 | 不支持 | 完全支持 |
| 透明效果 | 难实现 | 原生支持 |
| 内存占用 | 较低 | 较高(纹理缓存) |
| 适用场景 | 视频预览、游戏 | UI特效、滤镜预览 |
核心结论:如果你只需要简单显示摄像头画面,没有花哨的UI需求,SurfaceView是更高效的选择。如果需要做特效、动画、圆角等,那就用TextureView。
SurfaceView在Camera中的应用
在实际的Camera开发中,SurfaceView的使用频率其实更高。为什么?因为大部分场景就是「打开摄像头→预览画面→拍照/录像」,不需要什么花哨的动画效果。
我记得有一次做扫码功能,产品经理要求扫码框周围半透明,中间全透明。嗯,这个用SurfaceView就有点麻烦了。但如果是普通的预览界面,SurfaceView完全够用,而且性能更好。
来看一个典型的SurfaceView配合Camera2的用法:
// 创建SurfaceView
SurfaceView surfaceView = new SurfaceView(context);
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
// Surface已创建,可以开始预览
startCameraPreview(holder.getSurface());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// 尺寸变化时重新配置
configureCamera(width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// 释放Camera资源
closeCamera();
}
});
// 在Camera2中设置预览Surface
private void startCameraPreview(Surface surface) {
try {
CaptureRequest.Builder builder =
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
builder.addTarget(surface);
cameraCaptureSession.setRepeatingRequest(builder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
个人经验:surfaceCreated回调里不要做耗时操作。我曾经在这里面初始化Camera,结果导致ANR。正确的做法是在surfaceCreated里只做轻量操作,Camera的打开和配置放到异步线程去做。
性能考量:别被表象迷惑
很多人觉得TextureView功能多,那就无脑用TextureView呗。其实不然。性能上的差异在实际项目中是很明显的。
SurfaceView的优势在于:
- 零拷贝渲染:图像数据直接从Camera输出到Surface,不需要经过CPU
- 独立刷新率:不受View层级刷新频率影响
- 低延迟:预览延迟通常比TextureView低10-30ms
TextureView的代价在于:
- 纹理上传:每一帧都需要从GPU纹理读取再合成到View层级
- 内存开销:内部维护了一个Bitmap缓存,内存占用更大
- 帧率波动:在复杂UI场景下,帧率可能不稳定
我曾经在一个直播项目里做过对比测试。同样的720p预览,SurfaceView的CPU占用率比TextureView低了约15%,内存少了约20MB。对于低端机来说,这个差距是致命的。
避坑指南:如果你用TextureView做高帧率预览(比如60fps),记得在Android 7.0以上设备测试。低版本设备上TextureView的纹理合成效率很差,容易出现掉帧。我曾经在Android 6.0的设备上踩过这个坑,后来不得不换回SurfaceView。
如何选择?我的建议
嗯,这里我给出一个比较实用的选择策略:
- 纯预览场景(扫码、监控、直播推流):用SurfaceView,性能优先
- 带UI特效(美颜滤镜、AR贴纸):用TextureView,灵活性优先
- 需要截图/录屏:TextureView更方便,因为可以直接从View获取Bitmap
- 低端机适配:优先SurfaceView,减少性能压力
最后说一句,没有银弹。每个项目都有自己的特点,选型时要结合实际情况。我个人习惯是:默认用SurfaceView,除非有明确理由才换TextureView。这个原则帮我避免了很多性能问题。
一句话总结:SurfaceView是「快而糙」,TextureView是「慢而精」。选哪个,取决于你的场景更看重什么。