
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 渲染框架,核心设计亮点:
- 线程隔离:渲染逻辑在独立GLThread执行,避免阻塞UI线程;
- 灵活配置:通过工厂/选择器接口支持EGL上下文、配置、表面的自定义扩展;
- 生命周期联动:视图与EGL上下文、渲染线程的生命周期强绑定,减少资源泄漏;
- 性能适配:提供"按需渲染/持续渲染"两种模式,平衡性能与体验;
- 调试友好:内置GL调用错误检查和日志输出能力,降低调试成本。
