第2章 SurfaceView与TextureView生命周期差异:显示时机、缓冲机制、Surface复用策略

作为Android Surface体系的两大核心实现,SurfaceView 与 TextureView 在显示时机、缓冲模型以及Surface复用行为上存在本质差异。这些差异直接影响低延迟渲染、画面合成效率以及多窗口/折叠屏场景下的Surface生命周期边界管理。本章从原理出发,对比两者的生命周期节点,并给出可落地的代码示例与调试手段。

核心差异速览:SurfaceView拥有独立的Surface(由WMS管理),支持独立绘制线程和双缓冲机制;TextureView没有独立Surface,继承于普通View,通过SurfaceTexture与硬件合成器(HWC)交互,缓冲模型为单缓冲+同步等待。Surface复用策略前者随Activity pause/resume销毁重建,后者可借助SurfaceTexture预先创建并跨生命周期复用。

2.1 显示时机与生命周期对比

区别集中在Surface何时首帧可用、何时销毁,以及应用层回调的触发顺序。

项目 SurfaceView TextureView
Surface创建时机 窗口可见后,WMS分配Surface
surfaceCreated() 在首次可见时触发
View attach到窗口后,onSurfaceTextureAvailable() 在View布局完成后触发
首帧显示延迟 低(独立绘制线程无需等待UI线程) 中(需等SurfaceTexture更新+View重绘)
Surface销毁时机 Activity onPause → surfaceDestroyed(),不可见即销毁 Activity onPause → onSurfaceTextureDestroyed()(返回true则释放,false可延迟销毁)
Surface复用模式 逐帧不可复用,每次创建新Surface 可通过SurfaceTexture.release()后重新attach实现复用
缓冲区数量 通常2~3(双缓冲+备用) 通常1(SurfaceTexture内部单缓冲队列)
实践要点:
· SurfaceView的 surfaceCreated() 仅触发一次(除非Surface被销毁重建),TextureView的 onSurfaceTextureAvailable() 每次attach都可能触发,需额外判断是否首次可用。
· 在折叠屏或分屏切换时,SurfaceView可能因窗口可见性变化而频繁销毁/重建Surface,TextureView通过SurfaceTexture复用可减少此开销。

2.2 缓冲机制:双缓冲 vs 单缓冲同步

SurfaceView采用双缓冲(或三缓冲)机制,生产者(渲染线程)与消费者(SurfaceFlinger)之间通过dequeueBuffer/queueBuffer进行BufferQueue同步。TextureView则依赖SurfaceTexture的 单缓冲更新-等待-消费 模型,每次绘制必须等待上一个Buffer被消费者取走后才能更新。

从代码角度看,SurfaceView的绘制线程可以直接在Canvas上操作,而TextureView需要先锁定SurfaceTexture并执行 updateTexImage(),再通过View的invalidate触发onDraw进行渲染。

// SurfaceView 典型绘制循环(独立线程)
SurfaceHolder holder = surfaceView.getHolder();
Canvas canvas = holder.lockCanvas();   // 从BufferQueue中取出一个Buffer
// 绘制...
holder.unlockCanvasAndPost(canvas);    // 提交Buffer到队列

// TextureView 典型更新方式(需绑定到GLSurfaceView或自定义GL线程)
TextureView textureView = (TextureView) findViewById(R.id.texture);
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture st, int width, int height) {
        // 创建EGL上下文或MediaCodec输入Surface
        Surface surface = new Surface(st);
        // 后续通过 surface.drawCanvas() 或 MediaCodec 向SurfaceTexture生产数据
    }
    // ... 其他回调
});

2.3 Surface复用策略与生命周期边界

SurfaceView 的Surface由WMS管理,其生命周期严格绑定到窗口可见性。当Activity进入Pause状态且窗口不可见时,WMS会立即销毁Surface。应用层必须在 surfaceDestroyed() 中清理所有依赖该Surface的资源(如EGL context、MediaCodec)。

TextureView 的Surface本质是SurfaceTexture的包装,其生命周期由应用层控制。应用可以在 onSurfaceTextureDestroyed() 回调中返回 false 阻止立即释放,从而在Activity重建时复用同一个SurfaceTexture。

// TextureView - 主动复用SurfaceTexture
private SurfaceTexture mReusableTexture;

@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    // 返回false表示不释放,保留SurfaceTexture供后续复用
    mReusableTexture = surface;
    return false;
}

// 当Activity重新启动时,将复用的SurfaceTexture重新设置
if (mReusableTexture != null) {
    textureView.setSurfaceTexture(mReusableTexture);
} else {
    // 首次创建,走默认流程
}
常见错误:
1. 在SurfaceView的 surfaceDestroyed() 中未及时释放EGL context,导致下一次创建Surface时抛出 EGL_BAD_DISPLAY 或 EGL_BAD_SURFACE 错误。
2. 对TextureView的SurfaceTexture复用后未重新调用 setDefaultBufferSize(),导致新尺寸下渲染错位。
3. 在折叠屏展开时,TextureView的SurfaceTexture尺寸变化但未重新更新变换矩阵,画面拉伸或裁剪。

2.4 生命周期差异流程总览(SVG流程图)

下图直观对比SurfaceView与TextureView在Activity生命周期下的Surface创建/销毁路径,以及Surface复用策略的差异。

SurfaceView vs TextureView 生命周期流程对比 SurfaceView 流程 onCreate 注册Callback onResume 窗口可见 surfaceCreated surfaceChanged onPause 窗口隐藏 surfaceDestroyed TextureView 流程 onAttachedToWindow 布局完成尺寸确定 onSurfaceTextureAvailable onSurfaceTextureSizeChanged onPause:可阻止销毁 onSurfaceTextureDestroyed SurfaceView: 每次从onPause到onResume都会重新走surfaceCreated~surfaceDestroyed循环,Surface无法保留。 TextureView: 在onSurfaceTextureDestroyed中返回false可保留SurfaceTexture对象,重新setSurfaceTexture实现复用,避免重连开销。 左侧红色路径表示Surface必须销毁重建;右侧紫色路径表示开发者可以选择保留SurfaceTexture复用。

2.5 调试与常见错误排查

调试Surface生命周期常用的手段:

  • 开启 SurfaceFlinger dump 查看当前BufferQueue状态:
    adb shell dumpsys SurfaceFlinger --list
    adb shell dumpsys SurfaceFlinger | grep -A 20 "SurfaceView|TextureView"
  • 在回调中打印线程ID和时间戳,判断Surface创建/销毁是否在预期时机发生。
  • 使用 GPU渲染分析工具(Profile GPU Rendering)观察SurfaceView与TextureView的帧延迟差异。
常见错误案例分析
错误1:TextureView复用后未重新设置Buffer尺寸导致黑屏
解决:在 onSurfaceTextureAvailable() 中调用 surfaceTexture.setDefaultBufferSize(width, height) 强制匹配视图尺寸。
错误2:SurfaceView在surfaceDestroyed后继续渲染导致ANR或Crash
解决:在 surfaceDestroyed() 中设置标志位,while(!destroyed){ if(!holder.getSurface().isValid()) break; } 确保渲染线程安全退出。
错误3:两者混用时冲突
同一个窗口内同时使用SurfaceView和TextureView,可能导致SurfaceFlinger的Z-order计算异常,建议使用 android:outlineProvider 或调整层级。

2.6 性能与稳定性优化建议

  • 低延迟场景优先使用SurfaceView:独立双缓冲机制将渲染与UI线程解耦,适合相机预览、游戏等需要连续高帧率的场景。
  • 需要动画或变换效果时使用TextureView:继承View体系,支持平移、旋转、缩放动画,且Surface复用可减少Fragment切换时的闪黑。
  • TextureView复用带来的稳定性收益:在折叠屏展开/折叠、多窗口切换时,提前保留SurfaceTexture并重新attach,可避免重新初始化EGL或MediaCodec带来的耗时。
  • 缓冲区数量调优:SurfaceView可通过 holder.setFormat() 和 setFixedSize() 影响BufferQueue深度;TextureView可尝试通过 setOpaque(false) 减少合成开销。
本章最佳实践总结
1. 明确业务需求:需要低延迟 → SurfaceView;需要合成/动画/复用 → TextureView。
2. 在 onSurfaceTextureDestroyed 中返回false,并保存SurfaceTexture引用,在重建时使用 setSurfaceTexture() 进行复用。
3. 务必在surfaceDestroyed或onSurfaceTextureDestroyed中同步释放所有持有Surface/SurfaceTexture的资源,包括EGL context、MediaCodec、Canvas指针。