本文对于runOnGLThread方法的分析主要目的是为了帮助我的课程“拇指接龙游戏”学员更好地理解EasyNDK这个开源框架的使用。借助于这个框架,实现Android JAVA端与Cococ2d-x C++端交互,以及iOS Objective-C端与Cococ2d-x C++端交互将变得异常容易----极大地减少了相关的JNI编程与Objecive-C编程的代码量。但是,在EasyNDK的Android版本示例中在Java主Activity端存在如下代码段:
public void ChangeSomethingInCocos() { this.runOnGLThread(new Runnable() { @Override public void run() { AndroidNDKHelper.SendMessageWithParameters("ChangeLabelSelector", null); } }); } |
上述代码中SendMessageWithParameters方法为什么要运行于runOnGLThread方法中?本文将试图对这部分代码进行充分解释。至于EasyNDK的其他部分的使用就容易理解多了。
一、回顾传统Android编程中使用的runOnUiThread方法
在android中经常需要用到异步操作,Thread+Handler方式比较繁琐(当然功能也最为强大),而AsyncTask只能执行一次,很多需求不能满足(有关AsyncTask用法,请参考我引用的另外一篇博客)。
此时,我们可以试试Activity提供的另外一种简单的方法runOnUiThread,runOnUiThread可以帮助你在线程中执行UI更新操作。
以下为线程中使用runOnUiThread方法的框架代码:
MyActivity.this. runOnUiThread(new Runnable() { @Override public void run() { // refresh ui 的操作代码 } });
这里需要注意:runOnUiThread是Activity中的方法,在我们的工作线程中需要告诉系统是哪个Activity调用,所以前面显式地指明Activity。
下面为runOnUiThread的宏观代码:
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
从代码可以看到,runOnUiThread首先判断是否是UI线程,不是的话就post,如果是的话就正常运行该线程。只要经过主线程中的Handler.post或者postDelayed处理,线程Runnable都可以被加入到UI主线程的消息循环中,并为主线程的Handler所检索并调用。有关的Handler运行原理,请参考我引用的其他几篇博客文章。
值得注意的是,本节中的runOnUiThread方法适合于从子线程中更新普通Android View内容时调用。但是,当我们在Android下进行OpenGL ES开发时,也就是本文关注的cocos2d-x开发时,需要使用另一个方法,即下文要讨论的runOnGLThread。
二、Cocos2d-x编程中的runOnGLThread方法
cocos2d-x开发中,一旦涉及到Android平台移植自然要与Cocos2dxActivity这个东西打交道。Cocos2dxActivity是cocos2d-x开发团队的成果,它是一个抽象类,具体形式如下:
public abstract class Cocos2dxActivity extends Activity implements Cocos2dxHelperListener {//...}
我们知道,cocos2d-x是运行于OpenGL平台的。Android平台下进行OpenGL开发要与一个特殊View--GLSurefaceView打交道。GLSurefaceView要使用它自己的渲染器(Render)进行图形渲染,这个Render是运行于一个独立的区别于主GUI线程的子线程上的。
接下来,我们跟踪观察一下接口Cocos2dxHelperListener的定义(它隐藏于文件Cocos2dxHelper.java中):
public static interface Cocos2dxHelperListener { public void showDialog(final String pTitle, final String pMessage); public void showEditTextDialog(final String pTitle, final String pMessage, final int pInputMode, final int pInputFlag, final int pReturnType, final int pMaxLength); public void runOnGLThread(final Runnable pRunnable); }
其中的前两个方法与Cocos2d-x中显然对话框与文本框有关,在此我们省略有关讨论,而只专注于方法runOnGLThread。
接下来跟踪分析,Cocos2dxActivity中定义上述方法如下:
@Override public void runOnGLThread(final Runnable pRunnable) { this.mGLSurfaceView.queueEvent(pRunnable); }
有关Runnable对象,请参考我引用的其他几个博客文章。至于mGLSurfaceView,它是Cocos2dxGLSurfaceView类型的,而Cocos2dxGLSurfaceView是继承自GLSurfaceView的cocos2d-x开发团队的产品。当然,Cocos2dxGLSurfaceView内部也有一个自己的渲染器Cocos2dxRenderer,也是cocos2d-x开发团队在类GLSurfaceView.Renderer基础上稍微扩展的结果。
颇感遗憾的是,搜索网络上的资源,对于queueEvent方法的解释都不透彻。但是有几篇还是对上面这种表达进行了一定解释(由于本人当前没有时间研讨Android源码,只要先借鉴网络上看法)。说法是:GLSurfaceView的渲染线程与主GUI线程通信有多种方式,其实较为简单的一种便是使用上面的queueEvent方法,把一个Runnable对象投掷到主线程的消息队列中。简言之,通过上面的方法调用,这个Runnable实现的run方法将为主GUI线程对应的Handler所检索出并执行,从而实现了在GLSurfaceView的渲染子线程(Cocos2d-x C++这边)中更新主GUI线程界面内容的目的。至此,文章开始处的代码使用原因便得到解释。
另外一句话:Cocos2d-x中的runOnGLThread相当于普通Android Java环境下的runOnUiThread方法的功能,起到子线程更新GUI主线程界面之目的。
参考文章
1.【COCOS2DX-ANDROID-游戏开发之二二】之 调用Cocos2dxGLSurfaceView (http://blog.csdn.net/teng_ontheway/article/details/17267825)。
2.Android UI thread / main thread(http://blog.csdn.net/teng_ontheway/article/details/17267557)。
3.android中的runOnUiThread(runnable)
(http://blog.csdn.net/isming/article/details/8622087)。
4.Android开发笔记之:Handler Runnable与Thread的区别详解(http://www.jb51.net/article/37465.htm)。
5.Android的单线程模型(http://www.cnblogs.com/nio-nio/archive/2012/07/23/2604900.html)。
6.Android View.post(Runnable )(http://www.cnblogs.com/akira90/archive/2013/03/06/2946740.html)。
7.Android GLSurfaceView (http://blog.csdn.net/murongshusheng/article/details/7565771)。
8.android.opengl.GLSurfaceView概述 (http://blog.csdn.net/xqhrs232/article/details/6195824)。