30

When an application's process is killed, its activity stack is saved. Then when the application is restarted, all my activities resume and run into null pointers. Rather than modify every activity to accommodate this event, I would rather just have my application start with the base activity and not try to re-create the activity stack.

Is this possible?

I know about Intent.FLAG_ACTIVITY_CLEAR_TOP, but as I understand that will only kill activities after they have been re-created.

EDIT: is clearTaskOnLaunch what I want? I've added it to my default activity, but am seeing no effect. But this will kill my activities even if I just minimize the application, wont it? I'd rather only clear the stack if the entire process is rebooting.

EDIT 2: No, it's not what I want- an Android engineer gave a thorough response to some questions on how to use ClearTaskOnLaunch: http://groups.google.com/group/android-developers/browse_thread/thread/da024bcaf4e1960f/ab1e2b35c360cb65?pli=1

Nathan Fig
  • 14,970
  • 9
  • 43
  • 57
  • Does [finish()](http://developer.android.com/reference/android/app/Activity.html#finish%28%29) help solve your problem at all? It can be used to wipe the previous activity stack. – Matt Bishop Mar 24 '11 at 18:31
  • finish() is used to end an existing activity- I don't think it can be used to prevent an entire activity stack from being instantiated. – Nathan Fig Mar 24 '11 at 18:55
  • The reason I mention it is because calling `finish()` within your main activity's `onStop` event would clear the stack, however this being a good idea or not has been debated for some time. – Matt Bishop Mar 24 '11 at 19:04
  • Ahh, I see. That makes sense, though I was hoping for a way that would only clear the stack if the process itself is ended. – Nathan Fig Mar 24 '11 at 19:20
  • And you're sure all I have to do is call finish in the main activity's onStop event? I tried that and my activity stack is untouched. – Nathan Fig Mar 24 '11 at 19:57
  • 4
    First call `startActivity` with the new "root" activity you wish to have, then call `finish()`. That should reset your stack. – Matt Bishop Mar 24 '11 at 20:14
  • if you find any real solution tell me, i have the same issue with my activity stakc... – codeScriber Mar 24 '11 at 22:29
  • Yeah I'll let everyone know here if I find a solution... still searching myself. – Nathan Fig Mar 28 '11 at 13:22
  • The only solution I was able to find was to check a static variable in every instance of onCreate() and finish if that variable had been reset to null, indicating the task had been restarted. I close all activities down to my root activity and start over. – Nathan Fig Apr 04 '11 at 15:06
  • The bigger issue here is reliance on static state - you should use either save your state in the bundle or onto local disk. – eliasbagley Dec 20 '19 at 00:35

5 Answers5

10

The only solution I was able to find was to check a global static variable in every instance of onCreate() and finish if that variable had been reset to null, indicating the task had been restarted. I close all activities down to my root activity and start over.

Soon I hope to have my app at a point where it can save needed values in onPause(), but 'til then this is the only reliable way I know to work with lost initialization...

Nathan Fig
  • 14,970
  • 9
  • 43
  • 57
  • I create a global static variable in MainActivity as public static String isKilled = "isKilled", then check in HomeActivity at onCreate() like for instance if(MainActivity.isKilled == null) { finish(); }, but it always returns value, even when I enable 'Do not keep activities' > click Home > launch app or click Home > stop process from DDMS > launch app. Did you simulate this scenario by another way? Thanks – thanhbinh84 Dec 11 '13 at 10:12
  • 6
    I'm reading this answer over 4 years later, and thinking there MUST be a better way by now... – rmp251 Dec 09 '15 at 19:21
  • You can finish the activity before onSaveInstanceState is called (in this same method before the super call) on whatever condition you want. – Jacques Giraudel Dec 21 '15 at 18:06
  • See my answer for a better way by using a retained fragment rather than a static variable. – eliasbagley Dec 20 '19 at 00:33
3

If you're working with a spalshScreen or whatever LauncherActivity, you can create a Global static boolean and use it like this :

first find a way to store this static variable (maybe create a Global java file)

public abstract class Global {
    ...
    public static boolean processKilled = true;
    ...
}

then, in your laucherActivity (MainActivity or Splashscreen ...) add these lines :

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Global.processKilled = false;
        ...//your code here
    }

In fact, if your app process died it surely wont pass through the code of your launcherActivity. So the static boolean processKilled will remain true. Even if it does, that means your app is currently restarting and processkiled will be correclty set to true and all variables correclty instantiated (No NullPointerException)

By creating your own restartApp method you'll get what you want :

(in every activity you have add these lines :)

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (Global.processKilled){
            restartApp();
        }
        ...//your code here
    }

EDIT

if you're not a global variable-aholic, you may want to check if savedInstanceState is or isn't null...

Arnaud
  • 408
  • 4
  • 11
1

I use this piece of code:

public class NoRestoreActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Shoul be always NULL when created the first time, just in case...
        if (savedInstanceState != null && savedInstanceState.getInt("SavedInstance") > 0) {
            // ----- Your prefferred way to kill an application ----
            try {                
                this.finishActivity(0);               
            } catch (Exception ee) {
            }
            try {
                android.os.Process.killProcess(android.os.Process.myPid());
                System.exit(10);
            } catch (Exception eeee) {
            }
            return;
        }
        super.onCreate(savedInstanceState);
    }

    @Override
    protected void onSaveInstanceState (Bundle outState){
        super.onSaveInstanceState(outState);
        outState.putInt("SavedInstance",1);
    }
}
BCS Software
  • 1,745
  • 1
  • 11
  • 8
  • Note that this will also finish the activity on regular configuration changes, like screen orientation changes. OP is asking about the case of not restoring the activity after a process death. – eliasbagley Dec 19 '19 at 22:29
1

The solution above of relying on process death to reset a static variable works, but is a code smell due to it's reliance on static state. Here's a solution that has very similar properties, but doesn't rely on a static variable being reset after process death. Instead, it relies on the fact that after a process death any retained fragments will have their instance variables set back to default.

/**
 * This retained fragment functions as a detector for when the activity is restoring it's state
 * after a process death.
 *
 * <p>The callback onRestoreInstanceState cannot be used here, since that callback is also invoked
 * during regular activity recreation due to configuration changes, and there's no way to tell
 * whether the state is being restored from a configuration change or from recreation after process
 * death.
 *
 * <p>This retained fragment can be used to disambiguate these two scenarios. When the fragment is
 * created, it's {@code wasProcessKilled} flag is set to {@code false}, which will be retained
 * across activity restarts from configuration changes. However, on process rebirth, the retained
 * fragment will still be retained, but the value of {@code wasProcessKilled} will be set back to
 * its default value of {@code true}.
 */
public class ProcessDeathDetectorFragment extends Fragment {

  public static final String TAG = "ProcessDeathDetectorFragmentTag";

  public static ProcessDeathDetectorFragment create() {
    ProcessDeathDetectorFragment frag = new ProcessDeathDetectorFragment();
    frag.wasProcessKilled = false;
    frag.setRetainInstance(true);
    return frag;
  }

  private boolean wasProcessKilled = true;

  public boolean wasProcessKilled() {
    return wasProcessKilled;
  }

  @VisibleForTesting
  public void clear() {
    wasProcessKilled = true;
  }
}




private void closeActivityIfRestoredAfterProcessDeath(Bundle bundle) {
  FragmentManager fragmentManager = getSupportFragmentManager();
  ProcessDeathDetectorFragment retainedFragment =
      (ProcessDeathDetectorFragment)
          fragmentManager.findFragmentByTag(ProcessDeathDetectorFragment.TAG);

  if (bundle != null && retainedFragment != null && retainedFragment.wasProcessKilled()) {
    // If the bundle is non-null, then this is a restore flow.
    // If we are in a restore flow AND the retained fragment's wasProcessKilled flag is set back
    // to its default value of true, then we are restoring
    // from process death, otherwise the flag would have the value of false that was set when it
    // was created for the first time.
    finish();
    return;
  }

  if (retainedFragment == null) {
    fragmentManager
        .beginTransaction()
        .add(ProcessDeathDetectorFragment.create(), ProcessDeathDetectorFragment.TAG)
        .commit();
  }
}

@Override
protected void onCreate(Bundle bundle) {
  super.onCreate(bundle);
  closeActivityIfRestoredAfterProcessDeath(bundle);
  ...
}
eliasbagley
  • 1,237
  • 9
  • 17
0

Is this something one should take into account when dealing with native Android tools, or is this an issue brought up by third party task killers? At least on emulator using "force stop" seems to reset activity stack - which is something I've expected to happen always when application dies.

harism
  • 6,011
  • 1
  • 36
  • 31
  • It's something that should be anticipated with third party task killers, Android tools, and Android system processes. Killing a task from the ADB will clear the activity stack, yes, but there's a good chance that somewhere along the way, the Android OS will kill your task (to free memory, because user mount storage your task was using, etc.). – Nathan Fig Apr 04 '11 at 15:03
  • In other words, your activity should be able to shut down smoothly at any notice. I'm trying to find a way to do that by just restarting the task from scratch - I solved my own problem by just having onCreate() in every activity check a static variable to see if it was reset. – Nathan Fig Apr 04 '11 at 15:05