第1章:相机与SurfaceView/TextureView
大家好,我是老张。今天我们来聊聊Android摄像头开发里最基础也最绕不开的话题——SurfaceView和TextureView。说实话,我刚入行那会儿也被这两个东西搞晕过,明明都是用来显示画面的,为啥要搞两个?后来踩了不少坑才明白,这背后其实藏着Android渲染架构的设计哲学。
1.1 SurfaceView原理
SurfaceView,说白了就是一个独立的绘图表面。它不像普通View那样跟Activity共享同一个窗口,而是自己单独开了一个窗口来画东西。嗯,这里要注意,这个"独立"是它的核心特性。
核心要点:SurfaceView拥有独立的Surface,运行在独立的子线程中,与主UI线程分离。
为什么会这样设计?你想想看,摄像头预览需要30帧每秒的刷新率,如果跟UI混在一起,UI卡顿一下,画面就跟着掉帧,体验会很糟糕。我记得第一次做相机应用时,直接在UI线程里更新预览,结果滑动列表时画面直接卡成PPT……后来改用SurfaceView,问题就解决了。
SurfaceView的工作流程大致是这样的:
- SurfaceView创建时,会向WindowManager申请一个独立的Surface
- 这个Surface对应一个独立的窗口,拥有自己的Z-order
- Camera或MediaCodec可以直接往这个Surface上写数据
- SurfaceView内部通过双缓冲机制,避免画面撕裂
个人经验:SurfaceView的Surface创建时机比View的onCreate要晚。我曾经在onCreate里直接获取SurfaceHolder,结果拿到的是null。正确的做法是在SurfaceHolder.Callback的surfaceCreated回调里操作。
1.2 TextureView原理
TextureView跟SurfaceView不一样,它本质上是一个普通的View,只不过内部封装了一个Texture。说白了,它把摄像头的数据流当作纹理来处理,然后通过OpenGL渲染到屏幕上。
我个人习惯把TextureView理解为"会动的ImageView"。它没有独立的Surface,而是跟Activity共享同一个窗口。这意味着它可以像普通View一样做动画、做变换、做透明度调整——这些都是SurfaceView做不到的。
核心要点:TextureView基于硬件加速渲染,支持View的平移、缩放、旋转等变换操作。
TextureView的使用方式跟SurfaceView不太一样。它不需要SurfaceHolder,而是通过SurfaceTexture来接收数据流。我刚开始用的时候,总觉得少了点什么,后来才习惯这种回调模式。
// TextureView的典型用法
TextureView textureView = findViewById(R.id.texture_view);
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// 在这里打开相机,把surface传给Camera
mCamera = Camera.open();
try {
mCamera.setPreviewTexture(surface);
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// 处理尺寸变化
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// 释放相机
mCamera.stopPreview();
mCamera.release();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// 每帧更新时回调,慎用,性能开销大
}
});
避坑指南:我曾经在onSurfaceTextureUpdated里做图像处理,结果帧率直接掉到10帧。这个回调每帧都会触发,千万别在里面做耗时操作。如果需要做图像处理,建议用ImageReader。
1.3 两者对比与选择
好了,两个东西都讲完了,那到底该用哪个?我直接给你一张对比表,看完就明白了。
| 对比维度 | SurfaceView | TextureView |
|---|---|---|
| 渲染方式 | 独立窗口,直接渲染 | 共享窗口,纹理渲染 |
| 性能 | 高,延迟低 | 中等,有额外开销 |
| 动画支持 | 不支持View变换 | 支持平移/缩放/旋转 |
| Z-order | 固定,需setZOrderOnTop | 与普通View一致 |
| 截图 | 需要额外处理 | 支持getBitmap() |
| API等级 | API 1+ | API 14+ |
看完表格,你可能还是纠结。我直接给你一个选择建议:
- 做相机预览,追求性能 → 选SurfaceView。比如扫码、直播、视频通话,这些场景对延迟敏感,SurfaceView是首选。
- 需要做动画或特效 → 选TextureView。比如美颜相机、滤镜应用、AR效果,这些需要叠加UI动画的场景,TextureView更灵活。
- 需要截图或录制 → 选TextureView。它自带的getBitmap()方法可以直接拿到当前帧,省去很多麻烦。
- 兼容老设备 → 选SurfaceView。API 14以下的设备不支持TextureView,虽然现在很少见了,但如果你要兼容Android 4.0之前的设备,SurfaceView是唯一选择。
我的经验:如果项目没有特殊需求,我一般默认用SurfaceView。原因很简单——稳定。TextureView虽然功能多,但在一些低端机型上会有渲染延迟,特别是做全屏预览时,偶尔会出现画面撕裂。SurfaceView这么多年了,坑都被踩平了。
最后,我画了一张图,帮你理清这两个东西在Android渲染架构中的位置:
嗯,这张图应该能帮你理清思路了。SurfaceView走的是独立通道,TextureView走的是共享通道。选择哪个,取决于你的场景是追求性能还是追求灵活性。
总结一句话:性能优先选SurfaceView,功能优先选TextureView。如果两者都能满足需求,选SurfaceView,因为它更成熟、更稳定。
好了,这一章的内容就到这里。下一章我们会深入Camera2 API,看看怎么用新的方式控制相机。到时候你会发现,SurfaceView和TextureView的知识会派上大用场。
公众号:蓝海资料掘金营,微信deep3321