how to pause and resume a surfaceView thread
I have a surfaceView setup and running, but when I resume it I get an error that the thread has already been started. What's the proper way to handle when the app goes to the background and then back to the foreground? I've tinkered around and managed to get the app to come back without crashing... but the surfaceView doesn't draw anything anymore. My code:
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e("sys","surfaceCreated was called.");
if(systemState==BACKGROUND){
thread.setRunning(true);
}
else {
thread.setRunning(true);
thread.start();
Log.e("sys","started thread");
systemState=READY;
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e("sys","surfaceDestroyed was called.");
thread.setRunning(false);
systemState=BACKGROUND;
}
The easy solution is to simply kill and restart the thread. Create methods resume() - creates thread object and starts it - and pause() - kills thread (see Lunarlander example) - in your SurfaceView class and call these from surfaceCreated and surfaceDestroyed to start and stop the thread.
Now in the Activity that runs the SurfaceView, you will also need to call the resume() and pause() methods in the SurfaceView from the Activity's (or fragment's) onResume() and onPause(). It's not an elegant solution, but it will work.
This bug appears to relate to the lunar lander bug, which is quite famous (do a Google search on it). After all this time, and after several android version releases, the bug still exists and no one has bothered to update it. i have found this to work with the least code clutter:
public void surfaceCreated(SurfaceHolder holder) {
if (thread.getState==Thread.State.TERMINATED) {
thread = new MainThread(getHolder(),this);
}
thread.setRunning(true);
thread.start();
}
public void surfaceCreated(SurfaceHolder holder) {
if (!_thread.isAlive()) {
_thread = new MyThread(this, contxt);
}
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
_thread.setRunning(false);
while (retry) {
try {
_thread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
The best way I have found is to override the onResume method of the activity controlling the surface view so that with the method it re-instantiates the SurfaceView and then sets it with setContentView. The problem with this approach is that you need to reload any state that your SurfaceView was taking care of.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyCustomSurfaceView(this));
}
@Override
protected void onResume() {
super.onResume();
setContentView(new MyCustomSurfaceView(this));
}
Tried to comment on the accepted answer above but couldn't, new to this. I don't think you should be calling your start/stop thread methods from both your SurfaceView and Activity. This will result in starting/stopping the thread doubly, and you can't start a thread more than once. Just call your methods from the Activity's onPause and onResume. They're called when exiting and re-entering the app so this will make sure your states are handled properly. surfaceDestroyed isn't always called, which messed me up for a while.
If you use this method make sure to check for a valid surface in your run code before working with your canvas, because the Activity will start the thread in onResume before the surface is available.
while (_run) {
if (_surfaceHolder.getSurface().isValid()) {
...
}
} //end _run