Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is my runnable giving ANR?

Tags:

android

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?

like image 598
Thunder Rabbit Avatar asked Nov 04 '22 19:11

Thunder Rabbit


2 Answers

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:

  • one for input (the main activity which spends most of its time doing nothing)
  • one for game logic (a separate thread which is updated all the time)
  • one for drawing (which owns the canvas and is drawing all the time)
like image 149
f20k Avatar answered Nov 15 '22 11:11

f20k


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.

like image 32
Jeb Avatar answered Nov 15 '22 11:11

Jeb