4、SurfaceView与TextureView:SurfaceView原理与使用、TextureView原理与使用、两者对比与选型

做音视频开发,绕不开两个核心的显示组件:SurfaceView 和 TextureView。

我记得刚入行那会儿,领导丢给我一个播放器需求,我二话不说就用 SurfaceView 搞定了。结果产品经理跑过来说:“我要在视频上盖一个弹幕层,还要做旋转动画。” 我当时就懵了。嗯,这就是我今天要跟你聊的——这两个家伙到底有什么区别,什么时候该用谁。

一句话总结:SurfaceView 是“独立窗口”,性能好但难做动画;TextureView 是“普通 View”,灵活但费性能。

4.1 SurfaceView 原理与使用

4.1.1 原理:它其实是个“小窗口”

SurfaceView 的工作原理,说白了就是:它在 View 层级里挖了一个洞,然后在这个洞后面单独开了一个 Surface(绘图表面)。这个 Surface 由 WMS(WindowManagerService)管理,拥有独立的合成层。

为什么会这样?因为 Android 的 View 绘制是在主线程的 UI 渲染流程里走的。如果你直接在 View 上画视频帧,主线程稍微卡一下,画面就掉帧了。SurfaceView 绕开了这个限制——它自己开了一个子线程去渲染,跟主线程的 UI 绘制互不干扰。

我在项目中遇到过一个问题:用 SurfaceView 播放视频时,想在视频上显示一个自定义的进度条。结果发现进度条总是被视频画面盖住,或者闪烁。后来才明白,SurfaceView 的 Z 轴顺序是独立的,它默认在所有普通 View 之上。你需要用 setZOrderOnTop(false) 或者 setZOrderMediaOverlay(true) 来调整层级。

避坑指南:SurfaceView 的“洞”是异步创建的。你调用 getHolder() 拿到 SurfaceHolder 后,Surface 可能还没准备好。一定要在 surfaceCreated() 回调里才开始渲染。我曾经因为没等这个回调,直接往 Surface 上画图,结果黑屏了半小时才找到原因。

4.1.2 使用:三步走

使用 SurfaceView 其实很简单,核心就是跟 SurfaceHolder 打交道。我习惯把它拆成三步:

  1. 获取 SurfaceHolder:通过 getHolder() 拿到。
  2. 注册回调:实现 SurfaceHolder.Callback,监听创建、变化、销毁。
  3. 在回调里干活:在 surfaceCreated 里启动渲染线程,在 surfaceDestroyed 里停止。
// 典型用法
SurfaceView surfaceView = findViewById(R.id.surface_view);
SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // Surface 已就绪,可以开始渲染
        startRendering(holder.getSurface());
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // 尺寸或格式变了,更新渲染参数
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // Surface 即将销毁,停止渲染
        stopRendering();
    }
});

注意:渲染线程一定要在 surfaceCreated 里启动,在 surfaceDestroyed 里停止。不要在主线程里直接画,否则 SurfaceView 的优势就没了。

4.2 TextureView 原理与使用

4.2.1 原理:它就是个“普通 View”

TextureView 跟 SurfaceView 完全不同。它没有独立的窗口,而是作为一个普通的 View 存在于 View 层级中。它内部维护了一个 SurfaceTexture,用来接收视频帧,然后通过硬件加速渲染到 View 上。

你想想看,既然它是普通 View,那它就能享受 View 系统的一切待遇:可以平移、旋转、缩放、做动画,甚至可以用 setAlpha() 控制透明度。这就是为什么弹幕、字幕、UI 叠加层在 TextureView 上可以完美工作。

但代价是什么?性能。TextureView 的每一帧都要经过 View 的渲染管线,这意味着它比 SurfaceView 多了一次纹理上传和合成的开销。我在项目中做过对比,同样 1080p 的视频,TextureView 的 GPU 占用比 SurfaceView 高了 15%-20%。

个人经验:如果你要做视频编辑或者特效处理,TextureView 是更好的选择。因为它可以通过 getBitmap() 方法直接截取当前帧,方便做滤镜、截图等操作。SurfaceView 想截图?嗯,你得用 PixelCopy 或者自己读显存,麻烦得很。

4.2.2 使用:也是三步走

TextureView 的使用稍微有点不同,因为它没有 SurfaceHolder,而是直接操作 SurfaceTexture。

  1. 创建 TextureView:可以在 XML 里声明,也可以代码 new。
  2. 设置 SurfaceTextureListener:监听 SurfaceTexture 的可用状态。
  3. 在回调里绑定 MediaPlayer 或 Camera:把 SurfaceTexture 传过去。
// 典型用法
TextureView textureView = findViewById(R.id.texture_view);
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        // SurfaceTexture 已就绪,可以绑定播放器
        mediaPlayer.setSurface(new Surface(surface));
        mediaPlayer.start();
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        // 尺寸变化,调整播放器参数
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        // 返回 true 表示释放 SurfaceTexture
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        // 每帧更新时回调,可以用来做帧率统计
    }
});

注意:onSurfaceTextureDestroyed 的返回值很关键。返回 true 表示让 TextureView 自己释放 SurfaceTexture;返回 false 表示你还要继续用。我建议大部分场景返回 true,除非你有特殊需求。

4.3 两者对比与选型

好了,原理和用法都讲完了。现在我们来做个对比,看看什么时候该用谁。

对比维度 SurfaceView TextureView
渲染方式 独立 Surface,单独合成 通过 View 渲染管线合成
性能 高,延迟低 中等,多一次纹理上传
动画支持 差,不能做平移/旋转/缩放 好,支持所有 View 动画
层级控制 麻烦,需要 setZOrderOnTop 自然,跟普通 View 一样
截图 困难,需要 PixelCopy 简单,直接 getBitmap()
内存占用 高(需要额外纹理缓冲区)
API 级别 API 1 就有 API 14 引入

我个人习惯的选型原则是这样的:

  • 纯视频播放,没有复杂 UI 叠加:用 SurfaceView。性能好,省电,延迟低。
  • 需要弹幕、字幕、UI 控件覆盖在视频上:用 TextureView。省去层级调整的麻烦。
  • 要做视频编辑、滤镜、特效:用 TextureView。方便截图和纹理操作。
  • 低端机或追求极致性能:用 SurfaceView。TextureView 在低端机上容易掉帧。

我曾经踩过一个坑:在一个直播项目中,我用了 TextureView 来做连麦画面的显示。结果在部分华为手机上,画面延迟比 SurfaceView 高了 100ms 以上。后来换成 SurfaceView,延迟降下来了,但 UI 叠加层又得重新调层级。嗯,这就是典型的“没有银弹”——你得根据场景做取舍。

4.4 知识体系图

下面我用一张图来总结本章的核心逻辑,帮你理清思路:

SurfaceView vs TextureView 知识体系 Android 视频显示 SurfaceView 独立 Surface / 单独合成 高性能 / 低延迟 不支持动画 / 层级难控 TextureView 普通 View / 硬件加速渲染 支持动画 / 层级自然 性能开销大 / 可截图 选型:纯播放用 SurfaceView,有 UI 叠加用 TextureView

这张图把两个组件的核心差异和选型建议都串起来了。你记住一句话就行:SurfaceView 是性能优先,TextureView 是灵活优先。没有绝对的好坏,只有合不合适的场景。


好了,这一章的内容就到这里。希望你在实际项目中能根据需求做出正确的选择。如果遇到什么坑,欢迎随时交流。

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