返回

源码分析 | ThreadedRenderer 空指针问题,顺便把 Choreographer 认识一下

Android

ThreadedRenderer 空指针异常:深入浅出的分析与解决方案

在 Android 开发的浩瀚世界中,开发者时常会遇到各种各样的问题和异常,其中之一便是令人生畏的 ThreadedRenderer 空指针异常。本次,我们将踏上一次探索之旅,深入了解 ThreadedRenderer 的工作原理,分析异常背后的根源,并探讨切实可行的解决方案。

什么是 ThreadedRenderer?

ThreadedRenderer 是 Android 框架中一个至关重要的组件,负责在独立的工作线程上执行 OpenGL ES 绘制任务。其采用了双缓冲技术,即在工作线程上渲染一帧,而在主线程上显示另一帧。

ThreadedRenderer 的运作方式如下:

  1. SurfaceTexture 的创建与传递: 主线程创建一个 SurfaceTexture 对象并将其传递给 ThreadedRenderer。
  2. EGLSurface 的创建与附加: ThreadedRenderer 创建一个 EGLSurface 并将其附加到 SurfaceTexture 上。
  3. OpenGL 上下文的创建与渲染: ThreadedRenderer 在工作线程上创建 OpenGL 上下文并开始渲染。
  4. 帧的提交与显示: 渲染完成一帧后,ThreadedRenderer 将其提交给 SurfaceFlinger,后者负责在屏幕上显示该帧。
  5. 渲染结果的获取: 主线程通过 SurfaceTexture 从 ThreadedRenderer 获取渲染结果。

ThreadedRenderer 空指针异常的成因

根据本文开头提到的异常堆栈,问题根源在于 ThreadedRenderer 试图将一个空 SurfaceTexture 对象附加到 EGLSurface。这表明在传递给 ThreadedRenderer 之前,SurfaceTexture 对象已经遭到销毁。

问题的解决方案

为了解决这一问题,我们需要找出导致 SurfaceTexture 对象被销毁的原因。经调查,发现销毁操作发生在 onPause() 方法中,如下所示:

@Override
protected void onPause() {
    super.onPause();

    // 销毁 SurfaceTexture 对象
    mSurfaceTexture.release();
}

在 onPause() 方法中销毁 SurfaceTexture 是不恰当的,因为它将导致 ThreadedRenderer 在附加 SurfaceTexture 对象到 EGLSurface 时遭遇空指针异常。

正确的做法是在 onDestroy() 方法中销毁 SurfaceTexture 对象,如下所示:

@Override
protected void onDestroy() {
    super.onDestroy();

    // 销毁 SurfaceTexture 对象
    mSurfaceTexture.release();
}

Choreographer:主线程与工作线程的协作

在分析 ThreadedRenderer 空指针问题时,我们还遇到了另一个重要组件:Choreographer。Choreographer 负责协调主线程和工作线程之间的通信,并利用 VSYNC 信号同步两者的动作。

当 VSYNC 信号到来时,Choreographer 会触发 Choreographer.FrameCallback,该回调可以在主线程或工作线程上执行。

在我们的案例中,ThreadedRenderer 使用 Choreographer 同步其渲染任务。当 VSYNC 信号到达时,Choreographer 会触发 ThreadedRenderer 的 draw() 方法,后者在工作线程上执行。

总结

通过对 ThreadedRenderer 空指针问题的深入分析,我们不仅了解了 ThreadedRenderer 的工作原理,还领悟了 Choreographer 在 Android 系统中的重要作用。我们还探索了导致 SurfaceTexture 对象被销毁的根源,并找到了切实可行的解决方案。

通过这趟分析之旅,我们再次体会到扎实的基础知识和细致的排查过程对解决 Android 开发问题至关重要。希望本次分享能为开发者们提供有益的参考,助力大家攻克更多技术难关!

常见问题解答

  1. ThreadedRenderer 适用于哪些场景?
    ThreadedRenderer 适用于需要在独立线程上进行 OpenGL ES 绘制任务的场景,例如游戏、3D 渲染和视频播放。

  2. 如何避免在 onPause() 方法中销毁 SurfaceTexture 对象?
    正确的做法是在 onDestroy() 方法中销毁 SurfaceTexture 对象,以确保在 ThreadedRenderer 完成渲染任务后才执行销毁操作。

  3. Choreographer 的作用是什么?
    Choreographer 协调主线程和工作线程之间的通信,并使用 VSYNC 信号同步两者的动作,以确保流畅的视觉体验。

  4. 如何使用 Choreographer 同步渲染任务?
    可以通过实现 Choreographer.FrameCallback 接口并注册监听器的方式,在 VSYNC 信号到来时执行渲染任务。

  5. 解决 ThreadedRenderer 空指针异常有哪些其他技巧?
    除了本文提到的解决方案外,还可尝试检查 SurfaceTexture 对象是否在多个线程中使用,以及是否在 ThreadedRenderer 使用 SurfaceTexture 对象之前及时创建和附加 EGLSurface。