我的Android应用程序出现了问题(Android ANR keyDispatchingTimedOut)。我知道背后的原因,但我无法解决问题。我的UI线程可能已经超载,我想知道如何将昂贵的代码/方法移动到后台线程。我也想知道,什么样的图形/更新方法可以移动到后台线程?
这是我的UI线程:
Canvas canvas;
Log.d(TAG, "Starting game loop");
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
sleepTime = 0;
while (running) {
canvas = null;
// Try locking the canvas
try {
canvas = this.surfaceHolder.lockCanvas();
if (canvas != null) {
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// Update game state here!
this.gameView.update();
// Render state to the screen
// Draws the canvas on the panel
this.gameView.render(canvas);
// Calculate how long time the cycle took
timeDiff = System.currentTimeMillis() - beginTime;
// Calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
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) {
// Need to catch up by updating without rendering
// Update game state here!
this.gameView.update();
// Add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
} finally {
// In case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // End finally
}
以下是两个方法,update()和render()
public void update() {
// Gets the start level if it isn't initialized (when you
// enter the game for the first time)
if (startLvl == 0) {
startLvl = ((SingleGameActivity)getContext()).getStartLvl();
currentLvl = startLvl;
}
if (playing) {
// Update the life icons
updatePlayerLives();
updateComputerLives();
// Checks if you have won or lost
outOfBounds();
// Update the position of the ball, player and computer including
// collision detection and reaction.
ball.moveWithCollisionDetection(box, player, computer);
player.moveWithCollisionDetection(box);
computer.setDirectionWithAI(ball);
computer.moveWithCollisionDetection(box);
} else {
// Create new objects
newObjects();
if (newRound) {
playing = true;
newRound = false;
}
}
}
// //////////////////////////////////////////////////////////////////////////////////
// The render() method renders the UI graphics on the screen
public void render(Canvas canvas) {
super.onDraw(canvas);
if (playing) {
// Draw components
box.draw(canvas);
// Draw booster/s
if (racketTouches > 4) {
if (b1.isUsed()) {
b1 = new Booster();
}
canvas.drawBitmap(b1.booster, b1.boosterX, b1.boosterY, null);
}
if (racketTouches > 14) {
if (b2.isUsed()) {
b2 = new Booster();
}
canvas.drawBitmap(b2.booster, b2.boosterX, b2.boosterY, null);
}
if (racketTouches > 24) {
if (b3.isUsed()) {
b3 = new Booster();
}
canvas.drawBitmap(b3.booster, b3.boosterX, b3.boosterY, null);
}
// Draw rackets and ball
player.draw(canvas);
computer.draw(canvas);
ball.draw(canvas);
} else {
// Draw components
box.draw(canvas);
player.draw(canvas);
computer.draw(canvas);
ball.draw(canvas);
}
}
方法是否重载?如果是这样,我可以移动到后台线程?怎么样?
仅供参考:我正在创建一个包含助推器的复古乒乓球/网球比赛。
答案 0 :(得分:1)
您永远不想在UI线程上执行昂贵的任务,如绘图和动画。有两种方法可以利用工作线程......
带有runnable的线程(可以在绘图表面内创建,绘图表面必须实现Runnable):
new Thread(new Runnable() {
public void run() {
update();
render();
}
}).start();
在一个单独的类中扩展线程:
class myThread extends Thread {
Context context;
//Everything that you currently have in your UI thread should be in here...
/* Constructor for thread. Pass useful things like context, drawing surface, etc */
public myThread(Context context) {
this.context = context;
}
public void setRunning(Boolean running) {
this.running = running;
}
@Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
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
sleepTime = 0;
while (running) {
canvas = null;
// Try locking the canvas
try {
canvas = this.surfaceHolder.lockCanvas();
if (canvas != null) {
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// Update game state here!
this.gameView.update();
// Render state to the screen
// Draws the canvas on the panel
this.gameView.render(canvas);
// Calculate how long time the cycle took
timeDiff = System.currentTimeMillis() - beginTime;
// Calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
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) {
// Need to catch up by updating without rendering
// Update game state here!
this.gameView.update();
// Add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
} finally {
// In case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // End finally
}
}
}
创建绘图表面时,请指定线程:
Thread thread;
@Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new myThread(getContext());
thread.setRunning(true);
thread.start();
}
所以这就是它 - 请注意,您可能必须通过其构造函数将其他一些东西传递给第二个示例中的线程,以便它与您的游戏一起使用,例如您正在绘制的表面和SurfaceHolder
,但这就是主意。
祝你好运!