我正在尝试制作一个简单的飞机游戏,以建立我的技能并获得一些乐趣。但是,当我尝试将Thread
Looper
事件的消息传递给{{{}时,我遇到了包含onTouch
s,TextView
和消息队列的障碍1}}画我的飞机。我将尝试仅包含下面的基本代码,并使用“...”来表示省略的行。
我正在绘制一个独立的,老式的Android线程。这是Thread
:
Thread
在我展示run方法之前,请注意,public class BenThread extends Thread {
...
public BenThread(SurfaceHolder surfaceHolder, Context context,
BenSurfaceView surfaceView) {
this.surfaceHolder = surfaceHolder;
this.context = context;
this.surfaceView = surfaceView;
this.isRunning = false;
Bitmap planeImage = BitmapFactory.decodeResource(
context.getResources(), R.drawable.fighters);
airplane = new Airplane(50, 50, 2, 0, planeImage);
}
会在调用SurfaceView
时创建并启动Thread
。在我的主SurfaceChanged()
的{{1}}中,我创建了自定义onCreate()
的最终实例:
Activity
在UI布局中,SurfaceView
位于底部中心,并且final BenSurfaceView surfaceView = (BenSurfaceView) findViewById(R.id.surfaceView);
已连接。在TextView
中,对于OnTouchListener
,将调用以下行:
onTouch()
回到线程类,在run方法中创建此空消息的处理程序以及MotionEvent.ACTION_DOWN
创建:
surfaceView.thread.handler.sendEmptyMessage(1);
现在,如果我按原样运行,我会得到一个很好的飞机屏幕动画。
但是,当我单击底部的Looper
时,我会从日志中看到我向死线程发送了一条消息。好吧,我猜我在public void run() {
super.run();
Looper.prepare(); // Creates a Message Queue for the thread
MessageQueue queue = Looper.myQueue();
queue.addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {
Looper.myLooper().quit();
return false;
}
});
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i("HANDLING", "SOMETHING");
}
};
Looper.loop();
while (isRunning) {
currentTime = System.currentTimeMillis();
// spin in a while loop for a while
while ((currentTime - previousTime) < REFRESH_RATE) {
currentTime = System.currentTimeMillis();
}
airplane.move();
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
surfaceView.draw(canvas);
airplane.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
杀了它。所以现在让我评论退出方法:
Button
现在我的应用看起来不那么令人兴奋了:
但是,当我点击底部的IdleHandler
并查看日志时,有证据表明我的消息已被处理!所以最大的问题是,如何运行消息循环并仍然可以看到我的动画?
答案 0 :(得分:4)
调用Looper.loop()
后,在线程准备停止之前不应该返回。在 Looper.loop()
电话之后让游戏循环是没有意义的。那时线程已经死了#34;从某种意义上说,Looper不再收听消息了。
如果您希望线程在while (isRunning)
中运行,请执行此操作。如果您希望它是消息驱动的,那就这样做。不要试图在同一个线程中做两件事。 (请不要在CPU上旋转 - 在移动设备上快速耗尽电池。)
您可以在this article的附录A和B中找到有关游戏循环以及SurfaceView和线程的一些注释。在Grafika中使用Handler有各种动画渲染示例。例如,&#34;记录GL应用程序&#34;活动使用Choreographer在绘制时间时向渲染线程发送消息。
答案 1 :(得分:1)
这是因为IdleHandler()。它立即执行,退出你的looper。我评论了你的代码:
public void run() {
super.run();
Looper.prepare();
MessageQueue queue = Looper.myQueue();
queue.addIdleHandler(new IdleHandler() {
// This is executed immediately when the looper is idle.
// So this looper is quitted
// and thread starts to execute "while" loop
@Override
public boolean queueIdle() {
Looper.myLooper().quit();
return false;
}
});
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i("HANDLING", "SOMETHING");
}
};
Looper.loop(); // Take into account that this function is blocking
// This "while" loop is executed after the looper is quitted from IdleHandler
// So why your game is running
// When you click your button it tries to send message to quitted looper
// and you get the corresponding error message
while (isRunning) {
currentTime = System.currentTimeMillis();
// spin in a while loop for a while
while ((currentTime - previousTime) < REFRESH_RATE) {
currentTime = System.currentTimeMillis();
}
airplane.move();
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
surfaceView.draw(canvas);
airplane.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
当你评论IdleHandler时,线程执行Looper.loop()(它是阻塞)方法,因此没有到达while循环。
答案 2 :(得分:0)
在@fadden和@ armansimonyah13的澄清之后,我放弃了错误的尝试,在线程之间发送消息。在主Activity中,而不是像这样向线程发送消息:
surfaceView.thread.handler.sendEmptyMessage(1);
并尝试在线程中处理它,我只是更新线程中的公共数据(来自TextView的onTouch处理程序):
surfaceView.thread.airplane.setVelocity(8);
现在,当您单击底部的TextView时,平面会加速。这就是我想要的渲染 - 线程交互的UI线程。
顺便说一下,现在运行循环看起来像:
@Override
public void run(){
while (isRunning) {
airplane.move();
canvas = surfaceHolder.lockCanvas();
if (canvas != null) {
surfaceView.draw(canvas);
airplane.draw(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}