繁体   English   中英

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

[英]How to use Android's Looper in a drawing thread

我正在尝试制作一个简单的飞机游戏来提高自己的技能并找到一些乐趣。 但是,当尝试将消息从TextViewonTouch事件传递到绘制飞机的Thread时,我遇到了ThreadLooper和消息队列的onTouch 我将尝试仅在下面包含代码的基本位,并使用“ ...”表示省略的行。

我正在绘制一个单独的,良好的老式android线程。 这是Thread的构造函数:

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);
}

在显示run方法之前,请注意,有一个SurfaceView在调用SurfaceChanged()时创建并启动Thread 在主ActivityonCreate()中,创建自定义SurfaceView的最终实例:

final BenSurfaceView surfaceView = (BenSurfaceView) findViewById(R.id.surfaceView);

在UI布局中,位于底部中心的TextView带有一个OnTouchListener onTouch()用于MotionEvent.ACTION_DOWN ,以下行被称为:

surfaceView.thread.handler.sendEmptyMessage(1);

回到线程类,在run方法中创建此空消息的处理程序,并创建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中将其杀死的。 现在让我注释掉quit方法:

// Looper.myLooper().quit();

现在我的应用看起来不那么令人兴奋了: 在此处输入图片说明

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

调用Looper.loop() ,在线程准备停止之前,它不应返回。 Looper.loop()调用之后让游戏循环是没有意义的。 那时,该线程处于“死”状态,这意味着Looper不再侦听消息。

如果您希望线程在while (isRunning)运行,请执行此操作。 如果您希望它是消息驱动的,请执行此操作。 不要试图在同一线程中做这两个事情。 (并且请不要旋转CPU -在移动设备上会很快耗尽电池电量。)

你可以找到关于游戏圈的一些注意事项,以及有关SurfaceView和线程,在附录A且b 这篇文章 Grafika中有使用Handler进行动画渲染的各种示例。 例如,“记录GL应用程序”活动使用Choreographer在绘制时将消息发送到渲染线程。

这是因为IdleHandler()。 它立即执行,退出循环程序。 我评论了您的代码:

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循环。

在@fadden和@ armansimonyah13进行了澄清之后,我放弃了在线程之间发送消息的误导尝试。 在主活动中,而不是像这样向线程发送消息:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM