I'm trying to implement a SurfaceView
, calling its onDraw()
method from a Thread
, based on this tutorial. My app is giving ANR after I start the below code running.
I have tried implementing Runnable, instead of extending Thread
but it still gives ANR.
As far as I know there's nothing else running on the app when the ANR occurs. (The ANRs only started occurring when I added this code:)
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 ");
}
}
What's causing the ANR? Should I implement Runnable
or extend Thread
in a significantly different way?
gamePanel
extends from SurfaceView
and receives your input (through onTouchEvent()
). In other words, its your UI thread. When you are calling gamePanel.onDraw(canvas)
It is blocking (waiting for hardware to draw on the canvas
) and so you get ANR.
The MainThread
seems to be the rendering thread (seeing as how it owns the canvas
), and it should be the only one rendering. Your game logic should go in the MainGamePanel
or in a separate thread altogether.
So ideally I would have three threads:
Using Thread.sleep(...)
blocks the thread. If you block on the UI thread, your app is essentially frozen and the OS will consider it to be non-responsive. Instead, you should look into using Timer. Examples here and here.
Basically instead of sleeping your thread until it's next execution, you set a timer so you can be called when it's time to execute again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With