为什么我的Runnable会导致ANR?

5
我正在尝试实现一个SurfaceView,从Thread调用它的onDraw()方法,基于this tutorial。在我运行下面的代码后,我的应用程序会出现ANR。
我已经尝试过实现Runnable,而不是扩展Thread,但仍然出现ANR。
据我所知,当ANR发生时,应用程序上没有其他运行的内容。(只有在我添加了这段代码后才开始出现ANR)
package com.thunderrabbit.run;

import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class MainThread extends Thread {
    private final String TAG = this.getClass().getSimpleName();

    // desired fps
    private final static int    MAX_FPS = 50;
    // maximum number of frames to be skipped
    private final static int    MAX_FRAME_SKIPS = 5;
    // the frame period
    private final static int    FRAME_PERIOD = 1000 / MAX_FPS;  

    private SurfaceHolder surfaceHolder;
    private PageGLSurfaceView gamePanel;
    private boolean running;    // flag to hold game state
    private Canvas canvas;

    long beginTime;     // the time when the cycle begun
    long timeDiff;      // the time it took for the cycle to execute
    int sleepTime;      // ms to sleep (<0 if we're behind)
    int framesSkipped;  // number of frames being skipped 

    public MainThread(SurfaceHolder surfaceHolder, MySurfaceView gamePanel) {
        super();
        this.surfaceHolder = surfaceHolder;
        this.gamePanel = gamePanel;
    }

    public void setRunning(boolean running) {
        this.running = running;
    }

    @Override
    public void run() {
        sleepTime = 0;
        while(running)
        {
            canvas = null;
            try
            {
                synchronized (surfaceHolder)
                {
                    canvas = surfaceHolder.lockCanvas();
                    beginTime = System.currentTimeMillis();
                                    // resetting the frames skipped
                    framesSkipped = 0;
                    // update game state
                    this.gamePanel.update();
                    // render state to the screen
                    // draws the canvas on the panel
                    this.gamePanel.render(canvas);
                    // calculate how long did the cycle take
                    timeDiff = System.currentTimeMillis() - beginTime;
                    // calculate sleep time
                    sleepTime = (int)(FRAME_PERIOD - timeDiff);

                    if (sleepTime > 0) {
                        // if sleepTime > 0 we're OK
                        try {
                            // send the thread to sleep for a short period
                            // very useful for battery saving
                            Thread.sleep(sleepTime);
                        } catch (InterruptedException e) {}
                    }

                    while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
                        // we need to catch up
                        // update without rendering
                        this.gamePanel.update();
                        // add frame period to check if in next frame
                        sleepTime += FRAME_PERIOD;
                        framesSkipped++;
                    }
                    beginTime = System.currentTimeMillis();
                    gamePanel.onDraw(canvas);
                }
            }
            finally
            {
                if (canvas != null)
                {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
            // update game state
            // render state to the screen
        }
        DebugLog.d(TAG, "Game loop executed ");
    }
}

什么导致了ANR?我应该以显著不同的方式实现Runnable还是扩展Thread?

如果UI线程阻塞超过几秒钟,用户将看到臭名昭著的“应用未响应”(ANR)对话框。你知道“Activity.runOnUiThread(Runnable)”吗?我认为这将是你的解决方案。 - Balaji Khadake
@Balaji 我看过 runOnUiThread,但在这种情况下忘记了。另外,很抱歉我没有能够发布 LogCat 输出;基本上在再次引发问题之前,我已经解决了它。 :-) - Thunder Rabbit
那么,被选中的答案是如何解决你的问题的呢? - user65721
2个回答

1

使用Thread.sleep(...)会阻塞线程。如果您在UI线程上阻塞,您的应用程序基本上会被冻结,操作系统将认为它无响应。相反,您应该考虑使用计时器。示例此处此处

基本上,您不是让线程睡眠直到下一次执行,而是设置一个计时器,这样可以在需要执行时调用它。


1

gamePanel继承自SurfaceView并接收您的输入(通过onTouchEvent())。换句话说,它是您的UI线程。当您调用gamePanel.onDraw(canvas)时,它会阻塞(等待硬件在canvas上绘制),因此您会得到ANR。

MainThread似乎是渲染线程(因为它拥有canvas),并且它应该是唯一渲染的线程。您的游戏逻辑应放在MainGamePanel中或完全单独的线程中。

因此,理想情况下,我会有三个线程:

  • 一个用于输入(主要活动,大部分时间都在闲置)
  • 一个用于游戏逻辑(单独的更新线程)
  • 一个用于绘制(拥有画布并一直在绘制)

我遇到了同样的问题,但我的UI线程不是GamePanel,而是一个XML布局。 http://stackoverflow.com/questions/13128232/surfaceview-disappear-onresume - Issam Zoli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接