第1章:相机与SurfaceView/TextureView

大家好,我是老张。今天我们来聊聊Android摄像头开发里最基础也最绕不开的话题——SurfaceView和TextureView。说实话,我刚入行那会儿也被这两个东西搞晕过,明明都是用来显示画面的,为啥要搞两个?后来踩了不少坑才明白,这背后其实藏着Android渲染架构的设计哲学。

1.1 SurfaceView原理

SurfaceView,说白了就是一个独立的绘图表面。它不像普通View那样跟Activity共享同一个窗口,而是自己单独开了一个窗口来画东西。嗯,这里要注意,这个"独立"是它的核心特性。

核心要点:SurfaceView拥有独立的Surface,运行在独立的子线程中,与主UI线程分离。

为什么会这样设计?你想想看,摄像头预览需要30帧每秒的刷新率,如果跟UI混在一起,UI卡顿一下,画面就跟着掉帧,体验会很糟糕。我记得第一次做相机应用时,直接在UI线程里更新预览,结果滑动列表时画面直接卡成PPT……后来改用SurfaceView,问题就解决了。

SurfaceView的工作流程大致是这样的:

  1. SurfaceView创建时,会向WindowManager申请一个独立的Surface
  2. 这个Surface对应一个独立的窗口,拥有自己的Z-order
  3. Camera或MediaCodec可以直接往这个Surface上写数据
  4. 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渲染架构中的位置:

Android相机预览渲染架构 应用层 (Application) Camera API / CameraX / Camera2 选择 SurfaceView 还是 TextureView? SurfaceView 独立Surface → 独立窗口 SurfaceFlinger直接合成 低延迟,高性能 TextureView SurfaceTexture → 纹理 通过View渲染到窗口 支持动画/变换 SurfaceFlinger → 显示屏幕

嗯,这张图应该能帮你理清思路了。SurfaceView走的是独立通道,TextureView走的是共享通道。选择哪个,取决于你的场景是追求性能还是追求灵活性。

总结一句话:性能优先选SurfaceView,功能优先选TextureView。如果两者都能满足需求,选SurfaceView,因为它更成熟、更稳定。

好了,这一章的内容就到这里。下一章我们会深入Camera2 API,看看怎么用新的方式控制相机。到时候你会发现,SurfaceView和TextureView的知识会派上大用场。


公众号:蓝海资料掘金营,微信deep3321