3. Surface生命周期与Activity/Fragment生命周期的耦合:onResume/onPause、onStart/onStop对齐规则

3.1 生命周期耦合的本质

Surface的生命周期并非独立存在,而是与Activity或Fragment的窗口可见性及前台状态深度绑定。系统通过onResume/onPause、onStart/onStop回调向App传递“Surface可用性”的变化。理解这两层生命周期的对齐规则,是避免黑屏、闪退或内存泄漏的前提。

核心认知:onSurfaceCreated 一定发生在 onResume 之后、onPause 之前;onSurfaceDestroyed 一定发生在 onStop 之后、onDestroy 之前。但具体时序依赖于窗口焦点和硬件合成器的调度。
Activity 回调 Surface 回调对齐规则 典型触发场景
onCreate Surface 尚未创建(surface == null) App启动,Activity构造
onStart Surface 可能已创建(onSurfaceCreated 在 onResume 前刚完成) App从后台返回前台(未锁屏)
onResume 保证 Surface 已经创建且有效,可以开始渲染 用户可见且可交互
onPause Surface 仍然有效,但应立即停止提交新帧(渲染线程挂起) 按Home键、来电、跳转其他Activity
onStop Surface 可能即将销毁(onSurfaceDestroyed 在 onStop 之后不久触发) App完全不可见
onDestroy Surface 已销毁 Activity销毁/结束

3.2 对齐规则与实现流程

SurfaceView 和 TextureView 在实现上略有差异,但共同遵循以下对齐契约:

  1. 创建对齐:onSurfaceCreated 必须在当前 Activity 的 onResume 完成之前被调用。如果 onResume 返回后 Surface 仍无效,渲染器必须等待下一次 surfaceChanged。
  2. 暂停对齐:当 onPause 被调用时,渲染线程必须停止向 Surface 提交缓冲区。否则可能导致 dequeueBuffer 超时或 BufferQueue 溢出。
  3. 销毁对齐:onSurfaceDestroyed 在 onStop 之后,但可能在 onDestroy 之前。渲染器必须在收到该回调后释放所有 EGL 或 Vulkan 资源。
// 标准生命周期耦合代码(SurfaceHolder.Callback + Activity)
public class MainActivity extends AppCompatActivity {
    private SurfaceView surfaceView;
    private RenderThread renderThread;

    @Override
    protected void onResume() {
        super.onResume();
        // Surface 此时保证有效(若 SurfaceView 已附加到窗口)
        if (surfaceView.getHolder().getSurface().isValid()) {
            renderThread = new RenderThread(surfaceView.getHolder().getSurface());
            renderThread.startRendering();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 必须停止提交帧,避免 ANR
        if (renderThread != null) {
            renderThread.pauseRendering();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Surface 即将销毁,清理资源
        if (renderThread != null) {
            renderThread.releaseResources();
            renderThread = null;
        }
    }

    // SurfaceHolder.Callback
    private SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            // 通知渲染线程 Surface 已就绪
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            // 尺寸变化时重置视口
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            // 渲染线程必须在此回调返回前释放所有 GPU 资源
            if (renderThread != null) {
                renderThread.waitForFinish();
            }
        }
    };
}

3.3 时序对齐 SVG 图

Surface 生命周期与 Activity 生命周期对齐时序 Activity onCreate() onStart() onResume() onPause() onStop() onDestroy() Surface (SurfaceView) null(未创建) WSG 确认 onSurfaceCreated() onSurfaceChanged() 渲染开始 表面仍然有效 停止提交帧 onSurfaceDestroyed() 释放资源 null(已销毁) ⚠ 对齐事故 onPause后仍调用dequeueBuffer 导致 ANR 或 BufferQueue 溢出

3.4 常见错误与调试手段

错误模式 现象 根本原因 解决办法
渲染线程在 onPause 后仍提交帧 dequeueBuffer 返回 -EINTR,App 卡顿甚至 ANR 未使用 onPause 信号挂起渲染循环 在 onPause 中设置 renderPaused = true,渲染循环检查标志
onSurfaceDestroyed 后访问 Surface IllegalStateException: "Surface was already destroyed" 渲染线程未等待 surfaceDestroyed 回调完成 使用 CountDownLatch 确保资源释放后再返回
多窗口模式(split-screen)尺寸未重置 画面拉伸或裁剪 surfaceChanged 未同步更新视口 在 surfaceChanged 中立即调用 glViewport
TextureView 的 SurfaceTexture 生命周期不同步 黑屏或最后一帧冻结 未正确处理 onSurfaceTextureAvailable / onSurfaceTextureDestroyed 与 SurfaceHolder.Callback 类似的标志位管理,推荐使用 StateMachine

3.5 调试日志锚点

在关键生命周期方法中加入带时间戳的日志,便于对齐检查:

// 在 Activity 中
@Override
protected void onResume() {
    super.onResume();
    Log.d("LifecycleAlignment", "[Activity] onResume at " + System.nanoTime());
}

// 在 SurfaceHolder.Callback 中
@Override
public void surfaceCreated(SurfaceHolder holder) {
    Log.d("LifecycleAlignment", "[Surface] surfaceCreated at " + System.nanoTime() +
          "  (diff to onResume: " + (System.nanoTime() - lastResumeNs) / 1e6 + " ms)");
}
稳定性关键:在部分低端设备上,onSurfaceCreated 可能延迟到 onResume 之后 100~200ms 才触发。渲染器必须实现等待队列机制,而非假设 Surface 立即可用。推荐使用 CompletableFuture 或 PendingFrame 模式缓存渲染请求。

3.6 优化实践:对齐契约检查表

  • 创建检查:在 onResume 中通过 surface.isValid() 判断,若无效则暂缓渲染,等待 surfaceChanged。
  • 暂停检查:渲染循环每帧开始前检查 isPaused 标志,该标志由 onPause 设置为 true。
  • 销毁检查:onSurfaceDestroyed 回调内部使用同步屏障,确保所有 GPU 命令执行完毕才返回。
  • 多窗口适配:监听 onMultiWindowModeChanged 并重新设置缓冲区尺寸,避免 surfaceChanged 丢帧。
// 渲染线程的核心循环(伪代码)
while (running) {
    synchronized (pauseLock) {
        if (isPaused) {
            pauseLock.wait(); // 等待 onResume 唤醒
        }
    }
    if (!surface.isValid()) {
        continue; // 等待 surfaceChanged
    }
    // 锁定画布、渲染、提交
    Canvas canvas = surfaceHolder.lockCanvas();
    if (canvas != null) {
        doDraw(canvas);
        surfaceHolder.unlockCanvasAndPost(canvas);
    }
}

3.7 边界案例与性能影响

当 Activity 处于 onPause → onStop 过渡期间,Surface 可能仍然可见(如画中画或透明 Activity 叠加)。此时不应销毁渲染资源,仅停止提交新帧。
若渲染线程在 onPause 后立即释放 EGL 上下文,重新进入时需重建,导致额外 50~200ms 的停顿。优化策略:保留 EGL 上下文但暂停渲染循环,利用 eglMakeCurrent 切换。

最佳实践:采用“三态模型”——Active(渲染中)、Paused(Surface有效,渲染挂起)、Stopped(Surface无效,资源待释放)。状态转换由 Activity 生命周期回调驱动,避免对 Surface 回调的过度依赖。