欢迎光临
我们一直在努力

【Android GLSurfaceView源码学习】第五天:GLSurfaceView GLThread

在这里插入图片描述

GLThread核心:guardedRun函数

在Android OpenGL ES渲染体系中,GLThread是承载OpenGL渲染逻辑的核心线程,而guardedRun()则是GLThread的“心脏”——它掌控着EGL上下文/表面的生命周期、渲染回调的触发时机、线程状态的流转,甚至决定了整个OpenGL渲染流程的稳定性。

本文将从核心定位、设计思想、流程拆解到实际应用,全方位解析guardedRun()函数。

核心定位与作用

guardedRun()是GLThread类的私有核心方法,声明为:

private void guardedRun() throws InterruptedException

它的核心使命是管理OpenGL渲染的全生命周期,具体职责可归纳为4点:

  • 管理EGL上下文(Context)和EGL表面(Surface)的创建、销毁、重建与释放;
  • 处理渲染线程的暂停/恢复、Surface尺寸变化、Surface丢失/重建等状态变更;
  • 触发GLSurfaceView.Renderer的三大核心回调(onSurfaceCreated/onSurfaceChanged/onDrawFrame);
  • 处理EGL缓冲区交换(eglSwapBuffers)错误、EGL上下文丢失等异常,保证渲染线程的鲁棒性。
  • 简单来说,所有与OpenGL渲染相关的“调度”和“资源管理”逻辑,几乎都集中在这个函数中。

    核心设计思想

    guardedRun()的设计遵循两个关键原则,也是理解它的核心:

    1. 事件驱动 + 状态机

    函数内部通过“双层循环 + 标记位”实现状态机逻辑:

    • 外层无限循环:作为渲染线程的主循环,直到mShouldExit标记为true时退出;
    • 内层循环:优先处理事件队列(mEventQueue)、更新线程状态(暂停/恢复、Surface变化等),直到满足“可绘制”条件(readyToDraw()返回true);
    • 标记位驱动:通过mHaveEglContext/mHaveEglSurface/lostEglContext等布尔标记,跟踪EGL资源和线程状态,避免重复操作或状态混乱。

    2. 线程安全:全局锁同步

    整个核心逻辑包裹在sGLThreadManager的同步块中:

    synchronized (sGLThreadManager) { ... }

    sGLThreadManager是GLThread的全局管理器锁,保证多线程(如主线程、渲染线程)对EGL资源、状态标记的操作互斥,避免竞态条件。

    核心流程拆解

    我们将guardedRun()的执行流程拆解为6个核心阶段,结合关键代码逻辑逐一解析:

    阶段1:初始化与变量声明

    函数开头会初始化EGL辅助类,并声明一系列状态标记变量,这些变量是“状态机”的核心:

    // 初始化EGL辅助类(封装eglCreateContext/eglCreateSurface等底层操作)
    mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
    // EGL资源状态标记
    mHaveEglContext = false; // 是否拥有EGL上下文
    mHaveEglSurface = false; // 是否拥有EGL表面
    // 渲染通知标记
    mWantRenderNotification = false;
    // 其他核心标记(略)
    boolean createEglContext = false; // 是否需要创建EGL上下文
    boolean lostEglContext = false; // EGL上下文是否丢失

    关键细节:mGLSurfaceViewWeakRef是GLSurfaceView的弱引用——避免渲染线程持有View强引用导致内存泄漏,这是Android线程设计的经典实践。

    阶段2:内层循环:状态更新与事件处理

    内层循环是“状态预处理”阶段,核心做4件事:

    1. 处理退出与事件队列

    优先检查退出标记,再处理外部提交的事件(如主线程触发的“请求渲染”指令):

    if (mShouldExit) { return; } // 退出线程
    if (! mEventQueue.isEmpty()) {
    event = mEventQueue.remove(0);
    break; // 取出事件,跳出内层循环执行
    }

    2. 处理暂停/恢复逻辑

    同步“请求暂停状态”(mRequestPaused)与“当前暂停状态”(mPaused),暂停时释放EGL表面/上下文(可选):

    if (mPaused != mRequestPaused) {
    pausing = mRequestPaused;
    mPaused = mRequestPaused;
    // 暂停时释放EGL表面
    if (pausing && mHaveEglSurface) { stopEglSurfaceLocked(); }
    // 暂停时可选释放EGL上下文(由mPreserveEGLContextOnPause控制)
    if (pausing && mHaveEglContext && !preserveEglContextOnPause) {
    stopEglContextLocked();
    }
    }

    3. 处理Surface丢失/重建

    SurfaceView的Surface可能因系统回收、Activity生命周期变化丢失,函数会及时检测并释放EGL资源:

    // Surface丢失:释放EGL表面,标记“等待Surface重建”
    if ((! mHasSurface) && (! mWaitingForSurface)) {
    if (mHaveEglSurface) { stopEglSurfaceLocked(); }
    mWaitingForSurface = true;
    }
    // Surface重建:重置等待标记
    if (mHasSurface && mWaitingForSurface) {
    mWaitingForSurface = false;
    }

    4. 线程等待:无操作时进入休眠

    若没有事件、不满足可绘制条件,线程会释放锁并进入等待状态,避免空轮询消耗CPU:

    sGLThreadManager.wait();

    只有当外部调用notifyAll()(如Surface重建、收到渲染请求)时,线程才会被唤醒。

    阶段3:判断可绘制条件(readyToDraw())

    内层循环的核心出口是readyToDraw()返回true,该方法的判断逻辑(简化):

    private boolean readyToDraw() {
    return !mPaused // 未暂停
    && mHasSurface // Surface已存在
    && !mSurfaceIsBad // Surface未失效
    && mRequestRender; // 有渲染请求
    }

    满足条件后,函数会触发EGL资源创建(上下文/表面),并跳出内层循环执行渲染。

    阶段4:EGL资源创建

    这是渲染前的“准备工作”,分两步:

    1. 创建EGL上下文

    if (! mHaveEglContext) {
    mEglHelper.start(); // 内部调用eglCreateContext/eglMakeCurrent
    mHaveEglContext = true;
    createEglContext = true; // 标记需要触发onSurfaceCreated
    }

    2. 创建EGL表面

    if (mHaveEglContext && !mHaveEglSurface) {
    mHaveEglSurface = true;
    createEglSurface = true; // 标记需要创建EGL表面
    createGlInterface = true;// 标记需要创建GL10接口对象
    }

    EGL表面创建完成后,会通过mFinishedCreatingEglSurface标记通知其他线程。

    阶段5:触发渲染回调(核心渲染逻辑)

    EGL资源准备完成后,函数会按顺序触发Renderer的三大回调:

    1. onSurfaceCreated:初始化OpenGL资源

    if (createEglContext) {
    view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
    createEglContext = false;
    }

    仅在EGL上下文首次创建时触发,适合初始化纹理、着色器、投影矩阵等全局资源。

    2. onSurfaceChanged:适配Surface尺寸

    if (sizeChanged) {
    view.mRenderer.onSurfaceChanged(gl, w, h);
    sizeChanged = false;
    }

    Surface尺寸变化(如屏幕旋转、View大小调整)时触发,适合设置视口(glViewport)、调整投影矩阵。

    3. onDrawFrame:逐帧渲染

    view.mRenderer.onDrawFrame(gl);

    这是最核心的渲染逻辑入口,每帧都会执行——开发者的绘制代码(如绘制三角形、纹理贴图)都写在这里。

    阶段6:缓冲区交换与错误处理

    渲染完成后,需要将后台缓冲区的内容交换到前台显示(即“刷屏”):

    int swapError = mEglHelper.swap(); // 内部调用eglSwapBuffers
    switch (swapError) {
    case EGL10.EGL_SUCCESS:
    break; // 交换成功
    case EGL11.EGL_CONTEXT_LOST:
    lostEglContext = true; // 上下文丢失,后续重建
    break;
    default:
    // Surface失效,标记后触发重建
    mSurfaceIsBad = true;
    break;
    }

    关键细节:EGL上下文丢失(EGL_CONTEXT_LOST)是高频异常(如系统内存不足),函数会标记lostEglContext,后续自动重建上下文和表面,保证渲染不中断。

    阶段7:资源清理(finally块)

    无论函数因何退出(正常退出/异常),finally块都会释放EGL资源,避免内存泄漏:

    finally {
    synchronized (sGLThreadManager) {
    stopEglSurfaceLocked(); // 释放EGL表面
    stopEglContextLocked(); // 释放EGL上下文
    }
    }

    实际开发中的关键应用与问题排查

    理解guardedRun()的逻辑,能快速定位OpenGL渲染的常见问题:

    1. 渲染卡顿:检查线程等待与事件队列

    若渲染卡顿,可排查:

    • 事件队列(mEventQueue)是否堆积大量耗时操作;
    • readyToDraw()是否频繁返回false(如Surface频繁丢失、暂停状态异常);
    • onDrawFrame是否执行耗时操作(如复杂计算),阻塞渲染线程。

    2. EGL上下文丢失:定位资源重建逻辑

    若日志中出现egl context lost,说明guardedRun()检测到上下文丢失并触发重建,需检查:

    • 是否开启mPreserveEGLContextOnPause(暂停时保留上下文);
    • 设备内存是否充足(系统会回收后台App的EGL上下文);
    • onSurfaceCreated是否正确重新初始化OpenGL资源(上下文丢失后需重新加载纹理/着色器)。

    3. Surface创建失败:检查createSurface逻辑

    若mEglHelper.createSurface()返回false,会标记mSurfaceIsBad,需排查:

    • SurfaceView的Surface是否已销毁;
    • EGL配置(mEglConfig)是否与Surface匹配;
    • 权限是否缺失(如android.permission.WAKE_LOCK)。

    总结

    guardedRun()是Android OpenGL渲染的“总调度器”,其核心价值在于:

  • 以“事件驱动+状态机”的方式,优雅管理EGL资源的生命周期,避免资源泄漏;
  • 通过同步锁和状态标记,保证多线程环境下的渲染线程安全;
  • 内置异常处理机制(如上下文丢失、Surface失效),提升渲染稳定性;
  • 标准化Renderer回调触发时机,降低开发者的接入成本。
  • 在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » 【Android GLSurfaceView源码学习】第五天:GLSurfaceView GLThread
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址