GLSurfaceView 的基本使用
在Android 中使用OpenGL ES的过程中,就不得不提到一个控件:GLSurfaceView,本篇博文将结合几个简单的案例分别介绍GLSurfaceView的基本使用以及如何使用GLSurfaceView.Renderer所提供的接口将图形绘制到SurfaceView中。
OpenGL(Open Graphics Library)意为开放图形库,是一个跨平台的图形API,用于指定3D图形处理硬件中的软硬件编程接口。OpenGL一般用于图形工作站,PC端使用。由于性能和可移植性等各方面原因,在移动端使用起来相对比较麻烦。为此,Khronos公司就为OpenGL提供一个子集,OpenGL ES(OpenGL for Embedded System)。OpenGL ES是免费的跨平台且功能完善的2D/3D图形库接口API,是OpenGL的一个子集。
GLSurfaceView的特点:
1) GLSurfaceView继承SurfaceView,并实现了SurfaceHolder.Callback2接口。拥有SurfaceView的特性。如能在View的基础上创建独立的Surface,拥有SurfaceHolder来管理Surface,并且可以在子线程中进行View的渲染操作。通过SurfaceHolder得到Canvas,然后在单独的线程中利用Canvas绘制内容,然后更新到Surface。
2)GLSurfaceView在SurfaceView的基础上实现GLThread(EGLContext创建GL环境所在线程),可通过OpenGL进行绘制,可在SurfaceView所提供的Surface进行绘制。
3)支持按需渲染(on-demand)和连续渲染(continuous)两种模式。
1. GLSurfaceView 的使用步骤
使用GLSurfaceView时, 可以使用set方法来修改默认的属性,例如setRenderer(Renderer)设置渲染器,主要的工作就是设置Renderer,其他过程,例如EGL的创建,Surface的分配以及OpenGLES的调用都被隐藏起来了,GLSurfaceView会启动一个工作线程来完成渲染,避免阻塞UI主线程,这个工作线程就是mGLThread,这个线程在应用程序setRederer的时候启动,然后不停地等待和处理事件,同时还负责开展Render工作。本文使用的是 OpenGL ES 2 版本。
1)创建GLSurfaceView
在布局文件文件中加入GLSurfaceView,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.opengl.GLSurfaceView
android:id="@+id/glv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
然后在Activity的onCreate中实例化GLSurfaceView对象,在Activity或者Fragment的onResume方法和onPause方法中,分别调用GLSurfaceView的onResume和onPause方法。代码如下所示:
public class MainActivity extends AppCompatActivity {
GLSurfaceView glSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
glSurfaceView = findViewById(R.id.glv);
}
@Override
protected void onResume() {
super.onResume();
glSurfaceView.onResume();
}
@Override
protected void onPause() {
super.onPause();
glSurfaceView.onPause();
}
}
2)判断设备是否支持OpenGL ES2
public boolean supportES2() {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ConfigurationInfo deviceConfigurationInfo = am.getDeviceConfigurationInfo();
int reqGlEsVersion = deviceConfigurationInfo.reqGlEsVersion;
return reqGlEsVersion >= 0x2000;
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
glSurfaceView = findViewById(R.id.glv);
if(supportES2()){
// 设置 OpenGL ES 版本
glSurfaceView.setEGLContextClientVersion(2);
glSurfaceView.setRenderer(new MyRenderer());
}else {
Toast.makeText(this , "This device does not support OpenGL ES 2.0." ,
Toast.LENGTH_SHORT).show();
return;
}
}
3)初始化OpenGL ES 环境
GLSurfaceView默认情况下已经初始化好了OpenGL ES的运行环境,不需要做额外的工作就可以使用,当然也可以更改一些默认的设置。
4) 设置 Renderer
渲染是OpenGL ES 的核心工作,setRenderer 可以将用户自定义的Renderer 加入实际的渲染流程中,首先我们自定义MyRenderer类实现GLSurfaceView.Renderer接口,并实现onSurfaceCreated ,onDrawFrame,onSurfaceChanged方法,GLSurfaceView的渲染工作主要由GLSurfaceView.Renderer负责渲染,代码如下所示:
public class MyRenderer implements GLSurfaceView.Renderer {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Surface 创建时调用
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// Surface 改变时调用
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
// 执行渲染工作
GLES20.glClearColor(GLES20.GL_COLOR_BUFFER_BIT, 0f, 0f, 0f);
}
}
然后将MyRenderer设置给GLSurfaceView,代码如下:
glSurfaceView.setRenderer(new MyRenderer());
MyRenderer 给 GLSurfaceView 渲染一层黑色背景。
2. 使用GLSurfaceView绘制三角形
自定义TriangleRenderer并实现GLSurfaceView.Renderer 接口,代码如下所示:
public class TriangleRenderer implements GLSurfaceView.Renderer {
private int mProgram;
private FloatBuffer vertexBuffer;
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
// 三角形坐标位置。
static float triangleCoordinate[] = {
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
//设置颜色,依次为红、绿、蓝、透明度
float color[] = {1.0f, 0f, 0f, 1.0f};
static final int COORDINATE_PER_VERTEX = 3;
private int mPositionHandle;
private int mColorHandle;
//顶点个数
private final int vertexCount = triangleCoordinate.length / COORDINATE_PER_VERTEX;
//顶点之间的偏移量
private final int vertexStride = COORDINATE_PER_VERTEX * 4; // 每个顶点四个字节
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
//将背景设置为灰色
gl10.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
//申请底层空间
ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoordinate.length * 4);
bb.order(ByteOrder.nativeOrder());
//将坐标数据转换为FloatBuffer,用以传入OpenGL ES程序
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(triangleCoordinate);
vertexBuffer.position(0);
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
//创建一个空的OpenGLES程序
mProgram = GLES20.glCreateProgram();
//将顶点着色器加入到程序
GLES20.glAttachShader(mProgram, vertexShader);
//将片元着色器加入到程序中
GLES20.glAttachShader(mProgram, fragmentShader);
//连接到着色器程序
GLES20.glLinkProgram(mProgram);
}
public int loadShader(int type, String shaderCode) {
//根据type创建顶点着色器或者片元着色器
int shader = GLES20.glCreateShader(type);
//将资源加入到着色器中,并编译
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl10) {
//将程序加入到OpenGLES2.0环境
GLES20.glUseProgram(mProgram);
//获取顶点着色器的vPosition成员句柄
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
//启用三角形顶点的句柄
GLES20.glEnableVertexAttribArray(mPositionHandle);
//准备三角形的坐标数据
GLES20.glVertexAttribPointer(mPositionHandle, COORDINATE_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
//获取片元着色器的vColor成员的句柄
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
//设置绘制三角形的颜色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
//绘制三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
//禁止顶点数组的句柄
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
使用 TriangleRenderer,代码如下:
glSurfaceView.setRenderer(new TriangleRenderer());
运行效果如下:
2. GLSurfaceView源码解析
本文是基于 Android -29 来解析 GLSurfaceView 的源码。
2.1 入口 setRenderer 方法。
我们先从 setRenderer(Renderer renderer) 这个方法开始分析,代码如下所示:
public void setRenderer(Renderer renderer) {
// 状态监测
checkRenderThreadState();
if (mEGLConfigChooser == null) {
mEGLConfigChooser = new SimpleEGLConfigChooser(true);
}
if (mEGLContextFactory == null) {
mEGLContextFactory = new DefaultContextFactory();
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
// 将传入的 renderer 赋值给GLSurfaceView的成员变量 mRenderer
mRenderer = renderer;
// 创建GLThread线程
mGLThread = new GLThread(mThisWeakRef);
// 启动GLThread线程
mGLThread.start();
}
方法 checkRenderThreadState()
检查了 mGLThread 是否已经存在,存在则抛出异常;换句话说,我们不能在同一个GLSurfaceView
调用多次setRenderer(Renderer renderer)。
mEGLConfigChooser
、mEGLContextFactory
、mEGLWindowSurfaceFactory
是用户在setRenderer之前,可以调用相关方法来进行EGL设置,如果没有设置则采用默认实现。
mEGLConfigChooser
主要是指定了OpenGL ES一些颜色深度、缓存区深度的一些设定。 mEGLContextFactory
主要是提供了创建和销毁EGLContext上下文的一些处理。 mEGLWindowSurfaceFactory
主要是提供了创建和销毁EGLSurface的一些处理。
GLThread
就是我们一直所说的GL线程,主要是用于与OpenGL ES环境进行交互的线程。入参mThisWeakRef
是一个弱引用,指向了GLSurfaceView
本身。
接下来我们分析一下GLThread
这个GL线程,首先查看 GLThread 的 run 方法,代码如下所示:
public void run() {
setName("GLThread " + getId());
if (LOG_THREADS) {
Log.i("GLThread", "starting tid=" + getId());
}
try {
guardedRun();
} catch (InterruptedException e) {
// fall thru and exit normally
} finally {
sGLThreadManager.threadExiting(this);
}
}
在 run 方法中调用了 guardedRun方法,下面我们来查看 guardedRun 方法中的主要逻辑
1)创建一个 EglHelper 对象,EglHelper是一个封装了一些EGL通用操作的工具类。
mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
2)第一次循环时,会执行如下代码:
// If we don't have an EGL context, try to acquire one.
if(!mHaveEglContext){
if (askedToReleaseEglContext) {
askedToReleaseEglContext = false;
} else {
try {
// 开始初始化 OpenGL ES环境
mEglHelper.start();
} catch (RuntimeException t) {
sGLThreadManager.releaseEglContextLocked(this);
throw t;
}
mHaveEglContext = true;
createEglContext = true;
sGLThreadManager.notifyAll();
}
}
通过调用EglHelper.start()
进行OpenGL ES环境的初始化。然后将标示量mHaveEglContext 、createEglContext
设置为true。
下面我们查看 EglHelper 的 start 方法,代码如下所示:
public void start() {
...
// 获取一个EGL实例
mEgl = (EGL10) EGLContext.getEGL();
// 获取EGL显示设备
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
// 判断EGL显示设备是否正常
if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
// 存放EGL的主版本号和次版本号
int[] version = new int[2];
if (!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException("eglInitialize failed");
}
// 获取GLSurfaceView 的应用。
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view == null) {
mEglConfig = null;
mEglContext = null;
} else {
// 配置 EGLConfig 和 EGLContext
mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); // ... 1
}
if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
mEglContext = null;
throwEglException("createContext");
}
if (LOG_EGL) {
Log.w("EglHelper", "createContext " + mEglContext + " tid=" + Thread.currentThread().getId());
}
mEglSurface = null;
}
在注释1处,如果我们没有调用 setEGLContextFactory 这个方法(除非特殊需求,一般情况是不会调用),那么GLSurfaceView 会默认初始化 EGLContextFactory 为 DefaultContextFactory 类的对象。默认初始化是在 setRenderer方法中实现的。下面我们分析 DefaultContextFactory 类的 createContext 方法,代码如下所示:
//最简单的创建EGLContext的方式,如果需要用到多线程共享一个OpenGL ES环境的话,需要自己实现这个方法处理
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
EGL10.EGL_NONE };
return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
mEGLContextClientVersion != 0 ? attrib_list : null);
}
OpenGL ES环境的初始化就完成了。
回到GLThread 的 guardedRun 方法,代码如下:
// 当OpenGL ES环境初始化完成之后将createEglSurface和createGlInterface都设置为true
if(mHaveEglContext &&!mHaveEglSurface){
mHaveEglSurface = true;
createEglSurface = true;
createGlInterface = true;
sizeChanged = true;
}
//由上面代码可知 mHaveEglSurface == true
if(mHaveEglSurface){
//mSizeChanged是在SurfaceView回调surfaceChanged会设置会true
//首次初始化mSizeChanged默认为true
if (mSizeChanged) {
sizeChanged = true;
// 更新宽高
w = mWidth;
h = mHeight;
mWantRenderNotification = true;
if (LOG_SURFACE) {
Log.i("GLThread",
"noticing that we want render notification tid="
+ getId());
}
// Destroy and recreate the EGL surface.
createEglSurface = true;
mSizeChanged = false;
}
mRequestRender = false;
sGLThreadManager.notifyAll();
if (mWantRenderNotification) {
wantRenderNotification = true;
}
break;
}
上面的代码中我们主要关注 createEglSurface 变量,接下来我们分析下面这段代码:
if(createEglSurface){
if (LOG_SURFACE) {
Log.w("GLThread", "egl createSurface");
}
if (mEglHelper.createSurface()) { // ... 1
synchronized (sGLThreadManager) {
mFinishedCreatingEglSurface = true;
sGLThreadManager.notifyAll();
}
} else {
synchronized (sGLThreadManager) {
mFinishedCreatingEglSurface = true;
mSurfaceIsBad = true;
sGLThreadManager.notifyAll();
}
continue;
}
createEglSurface = false;
}
在注释1处调用 EglHelper 的 createSurface 方法,代码如下所示:
public boolean createSurface() {
...
// 如果已经创建过EglSurface,这里就先销毁
destroySurfaceImp();
// 获取GLSurfaceView的引用
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
//调用mEGLWindowSurfaceFactory工程创建一个EglSurface
//如果我们没有指定,会默认触发DefaultWindowSurfaceFactory的逻辑
mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
mEglDisplay, mEglConfig, view.getHolder());
} else {
mEglSurface = null;
}
// 检测是否创建EglSurface是否成功
if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
int error = mEgl.eglGetError();
if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {
Log.e("EglHelper", "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
}
return false;
}
// 将EglContext上下文加载到当前线程环境
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
logEglErrorAsWarning("EGLHelper", "eglMakeCurrent", mEgl.eglGetError());
return false;
}
return true;
}
继续查看 DefaultWindowSurfaceFactory
的createWindowSurface()方法,代码如下所示:
public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
EGLConfig config, Object nativeWindow) {
EGLSurface result = null;
try {
result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);
} catch (IllegalArgumentException e) {
Log.e(TAG, "eglCreateWindowSurface", e);
}
return result;
}
我们可以通过自己实现createWindowSurface()
方法实现我们需要的EGLSurface创建方式。
然后查看 destroySurfaceImp 方法的具体实现,代码如下:
private void destroySurfaceImp() {
if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
// 清理当前线程的EglContext 上下文环境
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_CONTEXT);
// 获取GLSurfaceView的引用
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
// 销毁前面创建出来的EGLSurface
view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
}
mEglSurface = null;
}
}
DefaultWindowSurfaceFactory
的逻辑就分析完了。
接下来我们回到 GLThread 的 guardedRun 方法,可以看到 createGlInterface
这个标示量的代码逻辑块,代码如下:
if(createGlInterface){
gl = (GL10) mEglHelper.createGL();
createGlInterface = false;
}
跟进查看 EglHelper 的 createGL 方法,代码如下:
GL createGL() {
// 获取 OpenGL ES 的编程接口
GL gl = mEglContext.getGL();
// 获取GLSurfaceView 的引用
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
//如果我们没调用setGLWrapper(GLWrapper glWrapper)这个接口,view.mGLWrapper == null
if (view.mGLWrapper != null) {
gl = view.mGLWrapper.wrap(gl);
}
...
}
return gl;
}
接下来我们回到 GLThread 的 guardedRun 方法,可以看到 createEglContext 这个标示量的代码逻辑块,代码如下所示:
if(createEglContext){
if (LOG_RENDERER) {
Log.w("GLThread", "onSurfaceCreated");
}
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceCreated");
view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); // ... 1
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
createEglContext = false;
}
在注释1处会回调到上层自定义的Renderer的onSurfaceCreated()方法
,说明GLSurfaceView
的GL环境已经准备完毕了。
接下来我们回到 GLThread 的 guardedRun 方法,可以看到 sizeChanged 这个标示量的代码逻辑块,代码如下所示:
if (sizeChanged) {
if (LOG_RENDERER) {
Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
}
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onSurfaceChanged");
view.mRenderer.onSurfaceChanged(gl, w, h); // ... 1
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
sizeChanged = false;
}
在注释1处会回调到上层自定义的Renderer的onSurfaceChanged()
,标识GLSurfaceView
的宽高已经发生改变。
上面整个流程已经将GLSurfaceView的EGL环境准备完毕了,接下来我们看看是怎么触发渲染的。
渲染业务还是在 GLTread 的 guardedRun 方法中,代码如下所示:
{
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "onDrawFrame");
view.mRenderer.onDrawFrame(gl); // ... 1
if (finishDrawingRunnable != null) {
finishDrawingRunnable.run();
finishDrawingRunnable = null;
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
在注释1处回调到上层Renderer的onDrawFrame()
实现,也就是说就能开始OpenGL ES的绘制工作。
2. 2 刷新模式
GLSurfaceView
有两种刷新模式:RENDERMODE_WHEN_DIRTY
和 RENDERMODE_CONTINUOUSLY
。
首先我们先看看RENDERMODE_CONTINUOUSLY
循环刷新模式。
先看看设置入口,代码如下所示:
public void setRenderMode(int renderMode) {
mGLThread.setRenderMode(renderMode);
}
继续跟踪 GLThread 的 setRenderMode 方法,代码如下所示:
public void setRenderMode(int renderMode) {
// 对传入的 renderMode 进行校验
if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
throw new IllegalArgumentException("renderMode");
}
synchronized(sGLThreadManager) {
// 将传入的 renderMode 赋值给 GLThread 的成员变量 mRenderMode
mRenderMode = renderMode;
sGLThreadManager.notifyAll();
}
}
搜索一下代码,我们发现mRenderMode在这个readyToDraw()方法
使用到,代码如下所示:
private boolean readyToDraw() {
//需要非暂停状态、EGLSurface已经创建、并宽高不为0
return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
&& (mWidth > 0) && (mHeight > 0)
//如果我们设置RENDERMODE_CONTINUOUSLY的话 这条件是成立的!
&& (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
}
继续搜索代码查看readyToDraw调用的地方,我们发现在 GLThread 的 guardedRun 方法中,代码如下所示:
while (true) {
if (readyToDraw()) {
if (!mHaveEglContext) {
//......
}
if (mHaveEglContext && !mHaveEglSurface) {
//......
}
if (mHaveEglSurface) {
//......
//触发一次后重置为false。
mRequestRender = false;
//......
break;
}
}
//......
sGLThreadManager.wait();
}
//......
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
try {
view.mRenderer.onDrawFrame(gl);
} finally {
}
}
可以得知,如果readyToDraw()
返回true的话,逻辑会走到break退出当前while循环。自然而然会调用到我们上层OpenGL ES的渲染实现。
如果mRenderMode == RENDERMODE_CONTINUOUSLY
的话,那么readyToDraw()
在成功初始化后返回 true。也就是在子线程运行时间里会不断while循环调用view.mRenderer.onDrawFrame()
这个回调。
那么当mRenderMode
的值为RENDERMODE_WHEN_DIRTY
时,由于mRequestRender默认为true,readyToDraw()第一次
返回了true,但是在GLThread 的 guardedRun 方法中的 if(readyToDraw())代码中赋值为 false了,
那么之后 readyToDraw()方法
返回了false,然后逻辑走到了sGLThreadManager.wait()
导致线程阻塞。
很明显,我们需要看看mRequestRender这个变量被赋值的位置,例如 requestRender 方法,代码如下所示:
public void requestRender() {
synchronized(sGLThreadManager) {
//重点这里,将mRequestRender设置为true,也就是说接下来readyToDraw()返回true
mRequestRender = true;
//通知guardedRun()里面的sGLThreadManager.wait()解除阻塞继续运行
sGLThreadManager.notifyAll();
}
}
可以得出结论,在RenderMode
的值为RENDERMODE_WHEN_DIRTY
时,我们调用一次requestRender()
的话,相对应的就能触发一次onDrawFrame()
进行OpenGL的渲染。
关于初始化和渲染触发的源码流程分析,就是上面这些了。
2.3 暂停与恢复
一般来说,GLSurfaceView 需要跟随Activity 或者 Fragment 的生命周期调用对应的onPause()
和onResume()
方法,代码最终会调用到GLThead
类的相关方法,
我们先看 onPause 方法,代码如下所示:
public void onPause() {
synchronized (sGLThreadManager) {
if (LOG_PAUSE_RESUME) {
Log.i("GLThread", "onPause tid=" + getId());
}
//将要暂停的状态标示量
mRequestPaused = true;
//通知GL线程解除阻塞
sGLThreadManager.notifyAll();
//这里是为了保证onPause()调用完毕后,GL线程已经是Paused状态了
while ((!mExited) && (!mPaused)) {
if (LOG_PAUSE_RESUME) {
Log.i("Main thread", "onPause waiting for mPaused.");
}
try {
sGLThreadManager.wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
接下来我们看 onResume 方法,代码如下所示:
public void onResume() {
synchronized (sGLThreadManager) {
if (LOG_PAUSE_RESUME) {
Log.i("GLThread", "onResume tid=" + getId());
}
//解除暂停的状态标示量
mRequestPaused = false;
//顺便请求重绘一次GL
mRequestRender = true;
mRenderComplete = false;
// 通知GL线程解除阻塞
sGLThreadManager.notifyAll();
//这里是为了保证onResume()调用完毕后,GL线程已经是Resume状态了
while ((! mExited) && mPaused && (!mRenderComplete)) {
if (LOG_PAUSE_RESUME) {
Log.i("Main thread", "onResume waiting for !mPaused.");
}
try {
sGLThreadManager.wait();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
我们看看这个状态是怎么工作的,还是在guardedRun()
这个方法里面,代码如下所示:
boolean pausing = false;
// 如果 mPaused != mRequestPaused 为true ,说明调用了暂停。
if (mPaused != mRequestPaused) {
pausing = mRequestPaused;
mPaused = mRequestPaused;
sGLThreadManager.notifyAll();
if (LOG_PAUSE_RESUME) {
Log.i("GLThread", "mPaused is now " + mPaused + " tid=" + getId());
}
}
// 如果调用了暂停并且 EglSurface 已经创建
if (pausing && mHaveEglSurface) {
if (LOG_SURFACE) {
Log.i("GLThread", "releasing EGL surface because paused tid=" + getId());
}
//销毁 EglSurface,并将mHaveEglSurface设置false
stopEglSurfaceLocked();
}
// 如果调用了暂停并且 EglContext 已经创建
if (pausing && mHaveEglContext) {
//销毁 EglContext,并将mHaveEglContext设置false
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
boolean preserveEglContextOnPause = view == null ?
false : view.mPreserveEGLContextOnPause;
if (!preserveEglContextOnPause) {
stopEglContextLocked();
if (LOG_SURFACE) {
Log.i("GLThread", "releasing EGL context because paused tid=" + getId());
}
}
}
stopEglSurfaceLocked()
最终会回调到destroySurfaceImp()方法,stopEglContextLocked()
最终会回调到EglHelper
的finish()方法
。
当我们调用了暂停时,mPaused
的值是true,所以readyToDraw方法会返回false,由上面可知逻辑将会阻塞住等待唤醒。
关于onResume()
就简单了,就是一次重新的EGL环境初始化过程,这里不做详细分析。