12

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 
RoflcoptrException
  • 51,941
  • 35
  • 152
  • 200
Chris
  • 187
  • 1
  • 1
  • 15
  • Yep, the error is caused by your `Thread`. Look at my answer below. – Wroclai Mar 15 '11 at 22:46
  • 1
    Both LunarLander and JetBoy crash after interrupeted by HOME and relaunched! – Lumis Mar 15 '11 at 23:01
  • 1
    SurfaceView / Activity lifecycle is discussed here: https://source.android.com/devices/graphics/architecture.html#activity . Examples of working code in Grafika are linked from http://stackoverflow.com/questions/21567641/should-the-renderingthread-of-a-surfaceview-have-the-same-life-cycle-as-the-view/21684399#21684399 – fadden Dec 24 '14 at 18:00

4 Answers4

20

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();
    }
}
Community
  • 1
  • 1
Wroclai
  • 26,835
  • 7
  • 76
  • 67
  • Viktor I cannot make your code work with the thread in the question. On surfaceDestroyed invokes the shutting down of the thread and runs every time the user leaves this activity, which includes when pressing Home button. – Lumis Mar 17 '11 at 09:42
  • @Lumis: You should not shut it down inside `surfaceDestroyed` since it's not needed (we're already pausing it in `onPause`. You should handle the `Thread` in `onResume`, `onPause` and `onDestroy`. – Wroclai Mar 21 '11 at 09:30
  • Used this snippet to great effect while working on a fraps like screen overlay for displaying dynamic data. Thanks a bunch @Wroclai :) – Sourav Banerjee Jul 06 '22 at 20:19
3

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();
}
Levon
  • 1,681
  • 2
  • 18
  • 40
Glen K
  • 31
  • 1
2

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.

Lumis
  • 21,517
  • 8
  • 63
  • 67
  • 2
    That's not a proper way to handle it. – Wroclai Mar 16 '11 at 09:56
  • 1
    If you know how why don't you edit the code in the question to make it work properly? Otherwise it is all theoretical... when I come up with a working code I will post it. – Lumis Mar 16 '11 at 10:25
  • @Lumis: See my answer and the link inside there. There's a solution there you have to manage the `Thread` correctly, using `synchronized` and locks. – Wroclai Mar 16 '11 at 10:28
  • What I think is that because the example posted in the question comes from Android SDK games, MoonLander and JetBoy, hence will be used by many people, we should write a working code/solution and post it here so that others don't have to reinvent the wheel and can learn from a hopefully good example. – Lumis Mar 16 '11 at 10:33
  • @Lumis: Then wait, I will post the whole solution. – Wroclai Mar 16 '11 at 10:37
  • @Lumis: Look at my answer now, there's the whole solution. – Wroclai Mar 16 '11 at 10:46
0

When you want to exit by clicking back button use this one...

boolean doubleBackToExitPressedOnce = false;
@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {

        //**completely exit the app like this**//
      
        System.exit(0);

        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Tap again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(() -> doubleBackToExitPressedOnce=false, 2000);
}