如何在绘图线程中使用Android的Looper

时间:2014-06-20 18:54:24

标签: java android multithreading surfaceview

我正在尝试制作一个简单的飞机游戏,以建立我的技能并获得一些乐趣。但是,当我尝试将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);

现在,如果我按原样运行,我会得到一个很好的飞机屏幕动画。 enter image description here 但是,当我单击底部的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

现在我的应用看起来不那么令人兴奋了: enter image description here

但是,当我点击底部的IdleHandler并查看日志时,有证据表明我的消息已被处理!所以最大的问题是,如何运行消息循环并仍然可以看到我的动画?

3 个答案:

答案 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();
        }
    }
}