【Android 美颜相机】第七天:GLTextureView 解析

GLTextureView

GLTextureView 是基于 Android 原生 TextureView 封装的 OpenGL ES 渲染视图组件,专为解决 OpenGL 渲染与 Android 视图系统整合设计,提供了灵活的渲染控制、EGL 上下文管理、渲染线程隔离等核心能力,相比传统 GLSurfaceView 更适配 TextureView 的纹理渲染特性。

包与导入声明

java 复制代码
package jp.co.cyberagent.android.gpuimage;

import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLDebugHelper; // OpenGL 调试辅助类
import android.util.AttributeSet;
import android.util.Log;
import android.view.TextureView; // 核心继承的视图类,支持纹理渲染
import android.view.View;

import java.io.Writer;
import java.lang.ref.WeakReference; // 弱引用,避免内存泄漏
import java.util.ArrayList;
import java.util.List;

// EGL 相关核心类,用于管理 OpenGL 上下文、显示、表面等
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
// OpenGL ES 核心接口
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;

关键导入说明

  • TextureView:作为基类,支持将 OpenGL 渲染结果输出为纹理,而非传统 SurfaceView 的独立窗口;
  • EGL 系列类:实现 OpenGL 上下文(EGLContext)、显示(EGLDisplay)、渲染表面(EGLSurface)的生命周期管理;
  • WeakReference:用于持有 GLTextureView 实例,避免渲染线程导致的内存泄漏。

类定义与核心常量

java 复制代码
/*
 * Copyright (C) 2018 Wasabeef
 * 开源协议:Apache License 2.0,允许自由使用、修改、分发
 */
public class GLTextureView extends TextureView
        implements TextureView.SurfaceTextureListener, View.OnLayoutChangeListener {

    // 日志标签,用于调试输出
    private final static String TAG = GLTextureView.class.getSimpleName();

    // 各类日志开关常量,用于控制不同模块的日志输出(开发调试用)
    private final static boolean LOG_ATTACH_DETACH = false;
    private final static boolean LOG_THREADS = false;
    private final static boolean LOG_PAUSE_RESUME = false;
    private final static boolean LOG_SURFACE = false;
    private final static boolean LOG_RENDERER = false;
    private final static boolean LOG_RENDERER_DRAW_FRAME = false;
    private final static boolean LOG_EGL = false;

    /**
     * 渲染模式:仅在表面创建/调用requestRender时渲染(按需渲染)
     * 优点:节省CPU/GPU资源,适合静态画面或低频更新场景
     */
    public final static int RENDERMODE_WHEN_DIRTY = 0;
    /**
     * 渲染模式:持续渲染(主动循环调用onDrawFrame)
     * 优点:画面实时更新,适合视频、动画等高频更新场景
     */
    public final static int RENDERMODE_CONTINUOUSLY = 1;

    /**
     * 调试标志:检查每个GL调用后的glError,若出错则抛出异常
     * 用于定位具体哪个OpenGL调用触发错误
     */
    public final static int DEBUG_CHECK_GL_ERROR = 1;
    /**
     * 调试标志:将所有GL调用日志输出到系统日志(verbose级别)
     * 用于跟踪OpenGL调用流程
     */
    public final static int DEBUG_LOG_GL_CALLS = 2;

    // 省略未展示的成员变量(如glThread、renderer、eglConfigChooser等)

核心设计说明

  • 继承 TextureView 并实现 SurfaceTextureListener:监听纹理表面的创建、销毁、尺寸变化;
  • 实现 OnLayoutChangeListener:监听视图布局变化,同步更新渲染表面尺寸;
  • 渲染模式常量:区分"按需渲染"和"持续渲染",适配不同性能/体验需求;
  • 调试标志:提供OpenGL调用的错误检查和日志输出能力,降低调试成本。

构造方法与生命周期基础

1 构造方法与初始化

java 复制代码
/**
 * 标准View构造方法(无属性集)
 * 必须调用setRenderer注册渲染器才能实现画面渲染
 */
public GLTextureView(Context context) {
    super(context);
    init();
}

/**
 * 标准View构造方法(带布局属性集)
 * 从XML布局加载时调用
 */
public GLTextureView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

/**
 * 初始化核心逻辑:设置SurfaceTexture监听器
 * 监听纹理表面的创建、销毁、更新等事件
 */
private void init() {
    setSurfaceTextureListener(this);
}

/**
 * 析构方法:确保渲染线程正常退出,避免内存泄漏
 * finalize是GC回收前的最后执行方法,兜底处理线程资源
 */
@Override
protected void finalize() throws Throwable {
    try {
        if (glThread != null) {
            // 若渲染线程未退出,请求退出并等待(避免线程残留)
            glThread.requestExitAndWait();
        }
    } finally {
        super.finalize();
    }
}

2 视图附着/分离窗口生命周期

java 复制代码
/**
 * 视图附着到窗口时调用
 * 核心逻辑:若视图曾分离且已有渲染器,重建渲染线程并恢复渲染模式
 */
@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    if (LOG_ATTACH_DETACH) {
        Log.d(TAG, "onAttachedToWindow reattach =" + detached);
    }
    if (detached && (renderer != null)) {
        // 恢复之前的渲染模式
        int renderMode = RENDERMODE_CONTINUOUSLY;
        if (glThread != null) {
            renderMode = glThread.getRenderMode();
        }
        // 重建渲染线程(弱引用持有当前视图)
        glThread = new GLThread(mThisWeakRef);
        if (renderMode != RENDERMODE_CONTINUOUSLY) {
            glThread.setRenderMode(renderMode);
        }
        glThread.start();
    }
    detached = false;
}

/**
 * 视图从窗口分离时调用
 * 核心逻辑:终止渲染线程,释放EGL上下文等资源
 */
@Override
protected void onDetachedFromWindow() {
    if (LOG_ATTACH_DETACH) {
        Log.d(TAG, "onDetachedFromWindow");
    }
    if (glThread != null) {
        // 请求渲染线程退出并等待,确保资源释放
        glThread.requestExitAndWait();
    }
    detached = true;
    super.onDetachedFromWindow();
}

核心设计要点

  • init() 方法统一初始化监听器,避免构造方法代码冗余;
  • finalize() 兜底回收渲染线程:防止视图未附着窗口时线程残留;
  • onAttachedToWindow/onDetachedFromWindow:管理渲染线程的生命周期,确保视图与线程资源联动。

核心配置方法

1 EGL上下文与渲染配置

java 复制代码
/**
 * 设置EGL上下文是否在暂停时保留
 * @param preserveOnPause true=保留上下文(减少恢复时的重建开销),false=释放(节省GPU资源)
 * 注意:保留上下文依赖设备支持(部分设备限制EGL上下文数量)
 */
public void setPreserveEGLContextOnPause(boolean preserveOnPause) {
    preserveEGLContextOnPause = preserveOnPause;
}

/**
 * 设置OpenGL上下文客户端版本(如2=OpenGL ES 2.0)
 * 必须在setRenderer之前调用,否则不生效
 * @param version 版本号(2=ES2.0,3=ES3.0)
 */
public void setEGLContextClientVersion(int version) {
    checkRenderThreadState(); // 检查渲染线程状态(确保未启动)
    eglContextClientVersion = version;
}

/**
 * 设置渲染器(核心方法)
 * 作用:注册渲染逻辑、初始化EGL相关工厂、启动渲染线程
 * 注意:生命周期内只能调用一次
 * @param renderer 自定义渲染器(实现Renderer接口)
 */
public void setRenderer(Renderer renderer) {
    checkRenderThreadState(); // 校验渲染线程未启动
    // 初始化默认EGL配置选择器(未自定义时)
    if (eglConfigChooser == null) {
        eglConfigChooser = new SimpleEGLConfigChooser(true);
    }
    // 初始化默认EGL上下文工厂
    if (eglContextFactory == null) {
        eglContextFactory = new DefaultContextFactory();
    }
    // 初始化默认EGL窗口表面工厂
    if (eglWindowSurfaceFactory == null) {
        eglWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
    }
    this.renderer = renderer;
    // 创建并启动渲染线程(弱引用持有当前视图,避免内存泄漏)
    glThread = new GLThread(mThisWeakRef);
    glThread.start();
}

2 渲染模式控制

java 复制代码
/**
 * 设置渲染模式(持续/按需)
 * 必须在setRenderer之后调用(依赖渲染线程)
 * @param renderMode RENDERMODE_CONTINUOUSLY/RENDERMODE_WHEN_DIRTY
 */
public void setRenderMode(int renderMode) {
    glThread.setRenderMode(renderMode);
}

/**
 * 主动请求渲染(适配RENDERMODE_WHEN_DIRTY模式)
 * 调用后触发一次onDrawFrame,更新画面
 */
public void requestRender() {
    glThread.requestRender();
}

核心设计要点

  • 配置方法的调用时序校验:checkRenderThreadState() 确保EGL配置、上下文版本等在渲染线程启动前设置;
  • 工厂模式设计:EGLConfigChooser/EGLContextFactory/EGLWindowSurfaceFactory 支持自定义,提升扩展性;
  • 渲染线程隔离:所有渲染逻辑在独立线程执行,避免阻塞UI线程。

核心内部接口

1 Renderer 渲染器接口

java 复制代码
/**
 * 渲染器核心接口:定义OpenGL渲染的三个核心生命周期方法
 * 所有渲染逻辑需实现此接口,并通过setRenderer注册
 */
public interface Renderer {
    /**
     * 表面创建/重建时调用(如EGL上下文丢失后恢复)
     * 用途:初始化OpenGL资源(纹理、着色器、VBO等)
     * @param gl OpenGL ES 1.0接口(可强转为GL11/GL20等更高版本)
     * @param config 当前EGL配置
     */
    void onSurfaceCreated(GL10 gl, EGLConfig config);

    /**
     * 表面尺寸变化时调用(如视图布局变化、屏幕旋转)
     * 用途:更新视口、投影矩阵等
     * @param gl OpenGL接口
     * @param width 表面宽度
     * @param height 表面高度
     */
    void onSurfaceChanged(GL10 gl, int width, int height);

    /**
     * 绘制单帧画面(核心渲染逻辑)
     * 持续渲染模式下会循环调用,按需渲染模式下仅requestRender/表面创建时调用
     * @param gl OpenGL接口
     */
    void onDrawFrame(GL10 gl);
}

2 EGL 扩展接口

java 复制代码
/**
 * EGL上下文工厂接口:自定义EGLContext的创建/销毁逻辑
 * 用于扩展上下文创建规则(如共享上下文、自定义属性)
 */
public interface EGLContextFactory {
    EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
    void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
}

/**
 * EGL窗口表面工厂接口:自定义EGLSurface的创建/销毁逻辑
 * 用于适配特殊的窗口表面(如纹理表面、PBuffer)
 */
public interface EGLWindowSurfaceFactory {
    EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config, Object nativeWindow);
    void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
}

/**
 * EGL配置选择器接口:自定义EGLConfig的选择逻辑
 * 用于筛选符合需求的EGL配置(如指定RGB位深、深度缓冲区大小)
 */
public interface EGLConfigChooser {
    EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
}

接口设计价值

  • 面向接口编程:将渲染逻辑(Renderer)、EGL资源管理(Factory/Chooser)抽象为接口,支持自定义扩展;
  • 生命周期对齐:Renderer的三个方法与EGL表面/上下文生命周期强绑定,符合OpenGL渲染最佳实践。

内部辅助类

1 默认EGL上下文工厂

java 复制代码
private class DefaultContextFactory implements EGLContextFactory {
    // EGL上下文客户端版本属性常量(对应OpenGL ES版本)
    private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;

    /**
     * 创建EGL上下文:根据指定的客户端版本(如2=ES2.0)创建上下文
     * @param egl EGL10实例
     * @param display EGL显示对象
     * @param config 选中的EGL配置
     * @return 创建的EGL上下文
     */
    public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
        // 构造上下文属性数组:指定OpenGL ES版本
        int[] attrib_list = {
                EGL_CONTEXT_CLIENT_VERSION, eglContextClientVersion, EGL10.EGL_NONE
        };

        // 创建上下文:无共享上下文(EGL_NO_CONTEXT),指定版本属性
        return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
                eglContextClientVersion != 0 ? attrib_list : null);
    }

    /**
     * 销毁EGL上下文:释放资源,若失败则抛出异常
     */
    public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
        if (!egl.eglDestroyContext(display, context)) {
            Log.e("DefaultContextFactory", "display:" + display + " context: " + context);
            if (LOG_THREADS) {
                Log.i("DefaultContextFactory", "tid=" + Thread.currentThread().getId());
            }
            EglHelper.throwEglException("eglDestroyContex", egl.eglGetError());
        }
    }
}

2 EGL配置选择器基类

java 复制代码
private abstract class BaseConfigChooser implements EGLConfigChooser {
    public BaseConfigChooser(int[] configSpec) {
        // 过滤配置:若为ES2.0,自动添加RENDERABLE_TYPE属性
        mConfigSpec = filterConfigSpec(configSpec);
    }

    /**
     * 核心选择逻辑:遍历EGL配置列表,选择符合条件的配置
     */
    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
        int[] num_config = new int[1];
        // 第一步:获取符合配置的EGLConfig数量
        if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, num_config)) {
            throw new IllegalArgumentException("eglChooseConfig failed");
        }

        int numConfigs = num_config[0];
        if (numConfigs <= 0) {
            throw new IllegalArgumentException("No configs match configSpec");
        }

        // 第二步:获取所有符合条件的EGLConfig数组
        EGLConfig[] configs = new EGLConfig[numConfigs];
        if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, num_config)) {
            throw new IllegalArgumentException("eglChooseConfig#2 failed");
        }
        // 第三步:子类实现具体的选择逻辑
        EGLConfig config = chooseConfig(egl, display, configs);
        if (config == null) {
            throw new IllegalArgumentException("No config chosen");
        }
        return config;
    }

    // 抽象方法:子类实现具体的配置筛选逻辑
    abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);

    protected int[] mConfigSpec;

    /**
     * 过滤配置:为ES2.0添加RENDERABLE_TYPE属性
     * 确保选中的EGLConfig支持OpenGL ES 2.0渲染
     */
    private int[] filterConfigSpec(int[] configSpec) {
        if (eglContextClientVersion != 2) {
            return configSpec;
        }
        int len = configSpec.length;
        int[] newConfigSpec = new int[len + 2];
        System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
        newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
        newConfigSpec[len] = 0x0004; /* EGL_OPENGL_ES2_BIT */
        newConfigSpec[len + 1] = EGL10.EGL_NONE;
        return newConfigSpec;
    }
}

辅助类设计价值

  • 模板方法模式:BaseConfigChooser 封装通用的EGLConfig查询逻辑,子类仅需实现具体筛选规则;
  • 版本兼容:filterConfigSpec 自动适配ES2.0的配置要求,降低开发者的版本适配成本;
  • 异常处理:明确的异常抛出,便于定位EGL配置选择失败的问题。

总结

GLTextureView 是一套高度封装的 OpenGL ES 渲染框架,核心设计亮点:

  1. 线程隔离:渲染逻辑在独立GLThread执行,避免阻塞UI线程;
  2. 灵活配置:通过工厂/选择器接口支持EGL上下文、配置、表面的自定义扩展;
  3. 生命周期联动:视图与EGL上下文、渲染线程的生命周期强绑定,减少资源泄漏;
  4. 性能适配:提供"按需渲染/持续渲染"两种模式,平衡性能与体验;
  5. 调试友好:内置GL调用错误检查和日志输出能力,降低调试成本。
相关推荐
honortech2 小时前
Android studio中配置gradle和对应的AGP版本
android·ide·android studio
廋到被风吹走2 小时前
【数据库】【MySQL】事务隔离深度解析:MVCC 实现与幻读解决机制
android·数据库·mysql
AC赳赳老秦2 小时前
技术文档合著:DeepSeek辅助多人协作文档的风格统一与内容补全
android·大数据·人工智能·微服务·golang·自动化·deepseek
赛恩斯2 小时前
安卓构建工具D8和R8的区别
android
—Qeyser2 小时前
Flutter CustomScrollView 自定义滚动视图 - 完全指南
android·flutter·ios
中达瑞和-高光谱·多光谱3 小时前
高光谱成像技术在汽车配件面漆颜色识别中的应用
数码相机
鸣弦artha3 小时前
Flutter 框架跨平台鸿蒙开发 —— Image Widget 图片处理:圆角、裁剪、阴影
android·flutter·harmonyos
—Qeyser3 小时前
Flutter ListView 列表组件完全指南
android·flutter·ios
sunfove3 小时前
波前、相位与振幅的数学本质及调制原理
数码相机·光学