Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

End SurfaceView and GameThread on exiting app

I've got a surfaceview and a gameThread class. The gameThread keeps updating and painting on the SurfaceView class.

Now when I exit the app (By pressing the home or back button) I get a message that the app force closed. That's because the GameThread still tries to draw on the erased surfaceview...

So how do i properly end the app without getting this force close notification? I would like the GameThread class to stop when the back button is pressed. It should pause when pressing on home and running in the background. When re-entering the still running game it should resume....

Any ideas?

This is my GameThread class:

public class GameThread extends Thread{

private GameView view;
public boolean isRunning = false;

public GameThread(GameView view) {
    this.view = view;
}

public void setRunning(boolean setRunning) {
    isRunning = setRunning;
}

public void run() {
    while(isRunning) {
        Canvas c = null;
        view.update();
        try {
            c = view.getHolder().lockCanvas();
            synchronized (view.getHolder()) {
                view.draw(c);
            }
        }finally {
            if(c != null) {
                view.getHolder().unlockCanvasAndPost(c);
            }
        }
    }
}

It keeps updating my GameView class:

public class GameView extends SurfaceView{

private GameThread gameThread;

public GameView(Context context, Activity activity) {
    super(context);
    gameThread = new GameThread(this);
    init();
    holder = getHolder();
    holder.addCallback(new SurfaceHolder.Callback() {

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {

        }
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            gameThread.setRunning(true);
            gameThread.start();

        }
        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height) {
        }
    });
}


public void init() {

}

public void update() {

}

public void draw(Canvas canvas) {
    super.draw(canvas);

}
}

When pressing home my logcat shows this up:

02-24 18:24:59.336: E/SurfaceHolder(839): Exception locking surface
02-24 18:24:59.336: E/SurfaceHolder(839): java.lang.IllegalStateException: Surface has already been released.
02-24 18:24:59.336: E/SurfaceHolder(839):   at android.view.Surface.checkNotReleasedLocked(Surface.java:437)
02-24 18:24:59.336: E/SurfaceHolder(839):   at android.view.Surface.lockCanvas(Surface.java:245)
02-24 18:24:59.336: E/SurfaceHolder(839):   at android.view.SurfaceView$4.internalLockCanvas(SurfaceView.java:872)

Main activity:

public class MainActivity extends Activity {

private RelativeLayout relativeLayout;
private GameView gameView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    gameView = new GameView(this, this);
    setContentView(R.layout.activity_main);
    relativeLayout = (RelativeLayout)findViewById(R.id.mainView);
    relativeLayout.addView(gameView);
}

@Override
public void onBackPressed() {
    // TODO Auto-generated method stub
    super.onBackPressed();
    gameView.onBackPressed();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

}
like image 943
user2410644 Avatar asked Feb 24 '14 17:02

user2410644


1 Answers

The Surface underlying the SurfaceView gets destroyed between onPause() and onDestroy() in the app lifecycle. Send a message to your renderer thread in onPause(), and wait for the thread to shut down.

For an example, see the "Hardware scaler exerciser" activity in Grafika. It starts the render thread in the SurfaceHolder's surfaceCreated() callback.

Update: it felt weird to start the thread in surfaceCreated() and stop it in onPause(), so after a bit of reshuffling HardwareScalerActivity now stops the thread in surfaceDestroyed(). (The SurfaceView documentation is a bit ambiguous -- it says the Surface is valid "between surfaceCreated() and surfaceDestroyed()" -- but looking at the SurfaceView implementation this appears to be the expected usage.)

Update 2: I updated the code some more after finding cases it didn't handle well. There's now a 60-line megacomment in the sources, which can also been seen in my answer to a similar question.

Update 3: The mega-comment turned into an architecture doc appendix. Grafika's "hardware scaler exerciser" activity demonstrates approach #2, while "texture from camera" demonstrates approach #1.

like image 162
fadden Avatar answered Oct 15 '22 22:10

fadden