Android crash when app is closed and reopened

I have a really simple android app that just displays a blank white screen. When I close the app by pressing the HOME button, then try to open the app again, it crashes and I get the "Force Close" button. In Eclipse I'm getting this error, "ActivityManager: Warning: Activity not started because the current activity is being kept for the user.". How do I fix this crash?

public class HelloAndroid extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(   WindowManager.LayoutParams.FLAG_FULLSCREEN,   
                            WindowManager.LayoutParams.FLAG_FULLSCREEN); 

    setContentView(new Panel(this));
}

class Panel extends SurfaceView implements SurfaceHolder.Callback {

    private TutorialThread _thread;

    public Panel(Context context) {
        super(context);

        // register our interest in hearing about changes to our surface
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        _thread = new TutorialThread(holder, this);

        setFocusable(true);
    }

    @Override
    public void onDraw(Canvas canvas) {

        // Clear the background
        canvas.drawColor(Color.WHITE);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // resize canvas here
    }

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

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // simply copied from sample application LunarLander:
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        _thread.setRunning(false);
        while (retry) {
            try {
                _thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // we will try it again and again...
            }
        }
    }
}

class TutorialThread extends Thread {
    private SurfaceHolder _surfaceHolder;
    private Panel _panel;
    private boolean _run = false;

    public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) {
        _surfaceHolder = surfaceHolder;
        _panel = panel;
    }

    public void setRunning(boolean run) {
        _run = run;
    }

    @Override
    public void run() {
        Canvas c;
        while (_run) {
            c = null;
            try {
                c = _surfaceHolder.lockCanvas(null);
                synchronized (_surfaceHolder) {
                    _panel.onDraw(c);
                }
            } finally {
                // do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null) {
                    _surfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }
}

}

Adding the LogCat

03-15 15:36:05.579: INFO/AndroidRuntime(4441): NOTE: attach of thread 'Binder Thread #2' failed
03-15 15:36:05.719: DEBUG/AndroidRuntime(4449): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
03-15 15:36:05.719: DEBUG/AndroidRuntime(4449): CheckJNI is OFF
03-15 15:36:05.719: DEBUG/dalvikvm(4449): creating instr width table
03-15 15:36:05.759: DEBUG/AndroidRuntime(4449): --- registering native functions ---
03-15 15:36:05.969: INFO/ActivityManager(1294): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.helloandroid/.HelloAndroid }
03-15 15:36:05.979: DEBUG/Launcher(1371): onPause+
03-15 15:36:05.979: DEBUG/Launcher.DragController(1371): +endDrag: false
03-15 15:36:05.979: DEBUG/Launcher.DragController(1371): mDragging == false
03-15 15:36:05.979: DEBUG/Launcher.DragController(1371): -endDrag: false
03-15 15:36:05.979: DEBUG/Launcher(1371): onPause-
03-15 15:36:05.999: DEBUG/AndroidRuntime(4428): Shutting down VM
03-15 15:36:05.999: DEBUG/AndroidRuntime(4449): Shutting down VM
03-15 15:36:05.999: WARN/dalvikvm(4428): threadid=1: thread exiting with uncaught exception (group=0x4001d7e0)
03-15 15:36:06.009: DEBUG/dalvikvm(4449): Debugger has detached; object registry had 1 entries
03-15 15:36:06.009: INFO/AndroidRuntime(4449): NOTE: attach of thread 'Binder Thread #3' failed
03-15 15:36:06.029: ERROR/AndroidRuntime(4428): FATAL EXCEPTION: main
03-15 15:36:06.029: ERROR/AndroidRuntime(4428): java.lang.IllegalThreadStateException: Thread already started.
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at java.lang.Thread.start(Thread.java:1322)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at com.example.helloandroid.HelloAndroid$Panel.surfaceCreated(HelloAndroid.java:55)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.SurfaceView.updateWindow(SurfaceView.java:538)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:206)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.View.dispatchWindowVisibilityChanged(View.java:3888)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:725)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:725)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.ViewRoot.performTraversals(ViewRoot.java:748)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1737)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.os.Handler.dispatchMessage(Handler.java:99)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.os.Looper.loop(Looper.java:123)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.app.ActivityThread.main(ActivityThread.java:4627)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at java.lang.reflect.Method.invokeNative(Native Method)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at java.lang.reflect.Method.invoke(Method.java:521)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at dalvik.system.NativeStart.main(Native Method)
03-15 15:36:06.039: WARN/ActivityManager(1294):   Force finishing activity com.example.helloandroid/.HelloAndroid
03-15 15:36:06.541: WARN/ActivityManager(1294): Activity pause timeout for HistoryRecord{450300c0 com.example.helloandroid/.HelloAndroid}
03-15 15:36:06.549: DEBUG/Launcher(1371): onResume+
03-15 15:36:06.549: DEBUG/Launcher.DragController(1371): +endDrag: false
03-15 15:36:06.549: DEBUG/Launcher.DragController(1371): mDragging == false
03-15 15:36:06.549: DEBUG/Launcher.DragController(1371): -endDrag: false
03-15 15:36:06.549: DEBUG/Launcher(1371): onResume-
03-15 15:36:08.645: ERROR/KINETO(1370): KLOG0C3- xmk_QueryOSQueue SDL Queue empty : WAIT_FOREVER 

Solution 1:

I've answered a question like this here.

The error you're getting is probably caused by your Thread (without seeing the full Logcat it's hard to tell though). You're starting it every time the surface is created, which will make your application crash because you can't call Thread.start() twice. Look at my link above for a more in-depth description of the problem and how you should solve it.

Since my explanation wasn't enough I will post the whole solution:

Inside your Runnable/Thread:

private Object mPauseLock = new Object();  
private boolean mPaused;

// Constructor stuff.      

// This should be after your drawing/update code inside your thread's run() code.
synchronized (mPauseLock) {
    while (mPaused) {
        try {
            mPauseLock.wait();
        } catch (InterruptedException e) {
        }
    }
}

// Two methods for your Runnable/Thread class to manage the thread properly.
public void onPause() {
    synchronized (mPauseLock) {
        mPaused = true;
    }
}

public void onResume() {
    synchronized (mPauseLock) {
        mPaused = false;
        mPauseLock.notifyAll();
    }
}

In your SurfaceView class:

private boolean mGameIsRunning;

@Override
public void surfaceCreated(SurfaceHolder holder) {
    // Your own start method.
    start();
}

public void start() {
    if (!mGameIsRunning) {
        thread.start();
        mGameIsRunning = true;
    } else {
        thread.onResume();
    }
}

Solution 2:

The solution below was tested. The code checks thread state, and if terminated creates a new one. No crashes, the only problem I can see is the game state is not saved so basically getting back from a HOME key press, starts the game again. p.s. remember to pass through the context from Lunarview view and set to mContextLunarView. Hope this helps. These forums are awesome. Keep it up.

public void surfaceCreated(SurfaceHolder holder) {
    // start the thread here so that we don't busy-wait in run()
    // waiting for the surface to be created    

    if(thread.getState() == Thread.State.TERMINATED) {
        //LunarView Thread state TERMINATED..make new...under CheckCreateThread

        thread = new LunarThread(holder, mContextLunarView, new Handler() {
            @Override
            public void handleMessage(Message m) {
                mStatusText.setVisibility(m.getData().getInt("viz"));
                mStatusText.setText(m.getData().getString("text"));
            }
        });     
    }   

    thread.setRunning(true);
    thread.start();
}

Solution 3:

Here is a simple solution which may be acceptable in some cases, like a background animation screen and activities which states do not need to be restored - the surface view activity needs to finish on pause.

protected void onPause()  { 
 super.onPause(); 
 finish();
} 

The better solution is to move the creation of the thread from the constructor into on surfaceCretaed like this:

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

Then in the thread loop create a pause flag:

if(!pause){
  _panel.onDraw(c); 
}

finally in onPause and onRestore for the activity set the pause flag:

   protected void onResume() {
            super.onResume();
            pause = false;    
        }

        protected void onPause()  {   
            super.onPause();   
            pause = true;   
        } 

When the user clicks Home button surfaceDestroyed is invoked which will shut down the current thread "_thread". When he returns to the app, surfaceCreated will assign the reference "_thread" to a new thread, while the old thread object will be removed by the garbage collector.