简体   繁体   中英

Game thread execution is not resuming properly in Android

I am developing a simple game that is running in its own thread. When I launch the app, everything works great. The thread is initialized, and the update() and render() functions are executed from the thread, which is great.

If I exit the app, the thread is paused. When I launch the app again, the thread resumes fine. (The update and render functions are executed). Again, this is great!

THE PROBLEM IS, I hit the power button to sleep the device. When I resume the app from this state, the render() function appears to work fine; it is drawing all of the elements (background, sprites, etc) exactly where they were before I hit the button. BUT: The sprites are no longer animated! After a bit of tracing and troubleshooting, I found out that although the "running" flag is set to TRUE, it seems like the "RUN" function is no longer running! (On an another note, I am confused why the RENDER function works, but the UPDATE function does not, unless the app is rendering the objects from some other method? Irregardless...)

I am working this problem from two possible angles; I am either missing something in the way Android manipulates my thread, OR would this have something to do with the fact that the member variables inside the objects have been reset...

Can someone please review the basic code I have posted below, and advise any suggestions on how to improve thread manipulation? I am an intelligent person, and I have read so many articles from different sources on the subject, and am more confused now more than ever! Am I on the right track here, or is there a more intelligent way of doing this? Do I really need a separate game thread, or can I run it using the device's main thread instead?

Thanks for your feedback!

public class GameThread extends Thread {
    private boolean running=false;
    private SurfaceHolder surfaceHolder;
    private GameView gameView;

    public GameThread(SurfaceHolder surfaceHolder, GameView gameView){
        super();
        this.surfaceHolder = surfaceHolder;
        this.gameView = gameView;
    }

    @Override
    public void run(){
        Canvas canvas;

        //  THIS ONLY RUNS ONCE!
        //  WHEN I TURN THE POWER OFF AND BACK ON, THE RUN() METHOD DOES NOT RUN!
        //  ODDLY ENOUGH, IF I WERE TO EXIT THE APP, AND GO BACK IN, IT WORKS FINE!
        //  THE running FLAG SEEMS TO CHANGE PROPERLY WHEN PAUSING OR RESUMING (SEE gameView, BELOW).
        while (running){
            canvas = null;
            try {
                canvas = this.surfaceHolder.lockCanvas();
                synchronized (surfaceHolder){
                    gameView.update();
                    gameView.render(canvas);
                }
            } finally {
                if (canvas!=null)
                    surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }
    public void setRunning(boolean running){
        this.running = running;
    }
}

public class GameView extends SurfaceView implements SurfaceHolder.Callback {
    private GameThread thread;
    private GameScreen gameScreen;

    public GameView(Context context) {
        super(context);
        gameScreen = new GameScreen(context);
        setFocusable(true);
        thread = new GameThread(getHolder(), this);
        getHolder().addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        thread = new GameThread(getHolder(), this);
        thread.setRunning(true);
        thread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        boolean retry = true;
        while (retry){
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e){
                // Do nothing; continue trying
            }
        }
    }

    public void render(Canvas canvas){
        gameScreen.draw(canvas);
    }   

    public void update(){   
        gameScreen.update();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        gameScreen.touch(event);
    }

    public void resume(){
        // TODO: Resume events go here
        thread.setRunning(true);
    }

    public void pause(){
        // TODO: Pause events go here
        thread.setRunning(false);
    }
}

public class GameScreen {
    public GameScreen(Context context){
        // Initialize private variables here
    }

    public void draw(Canvas canvas){
        // Draw events go here
    }

    public void update(){
        // Update events go here
    }

    public void touch(MotionEvent event){
        // Touch events go here
    }
}

The update() method needs to be executed BEFORE trying to post the canvas. Still unsure why this is, but it works as intended now.

    Canvas canvas = null;
    while (running){
        gameView.update();

        try {
            canvas = this.surfaceHolder.lockCanvas();
            synchronized (surfaceHolder){

                gameView.render(canvas);
            }
        } finally {
            if (canvas!=null)
                surfaceHolder.unlockCanvasAndPost(canvas);
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM