我正在尝试实施SurfaceView
,根据this tutorial从onDraw()
调用Thread
方法。在我开始运行以下代码后,我的应用程序正在提供ANR。
我尝试了implementing Runnable,而不是扩展Thread
,但它仍然提供了ANR。
据我所知,当ANR发生时应用程序上there's nothing else running。 (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
吗?
答案 0 :(得分:1)
使用Thread.sleep(...)
阻止线程。如果您阻止UI线程,您的应用程序基本上被冻结,操作系统将认为它没有响应。相反,你应该考虑使用Timer。示例here和here。
基本上不是在你的线程下次执行之前休眠,而是设置一个计时器,以便在需要再次执行时调用它。
答案 1 :(得分:1)
gamePanel
从SurfaceView
延伸并接收您的输入(通过onTouchEvent()
)。换句话说,它是你的UI线程。当你打电话gamePanel.onDraw(canvas)
它正在阻塞(等待硬件在canvas
上绘制),所以你得到了ANR。
MainThread
似乎是渲染线程(看看它如何拥有canvas
),它应该是唯一一个渲染。您的游戏逻辑应该放在MainGamePanel
或完全单独的线程中。
理想情况下,我会有三个主题: