Intent not restored correctly after activity is ki

2019-03-09 13:20发布

问题:

In my application there is an activity started using the FLAG_ACTIVITY_SINGLE_TOP and FLAG_ACTIVITY_CLEAR_TOP flags because I want to make sure that only one instance of that activity is at the top of the stack and all activities on top of the old instance are closed. So far so good.

Next I wanted to test if the activity restores correctly after being created more than once and successively destroyed. I take care to manually set the intent using Activity.setIntent() when Activity.onNewIntent() is called so that the most recent intent is returned by Activity.getIntent(). In order to test that I activated the "Don't keep activities" option in the developer options, but the intent returned by Activity.getIntent() when the activity is re-created is the very first intent that created it and not the most recent one.

This happens on JB and ICS, I haven't tested it on older versions. Am I doing something wrong or did I misunderstand something in the docs?

回答1:

If you kill your app while it is in the foreground, this is not the same as when Android kills your app (which it will only do when your app is in the background). If you kill and then restart the app, it is like starting it all over again from scratch. There is no "restore" going on here. If you add logging to onCreate() you should see that after you kill and restart your app, the Bundle that is passed to onCreate() is null.

Unfortunately it is pretty difficult to simulate what happens when Android kills your app.

EDIT: Added more stuff after OP's comment

Here's a concrete example for discussion purposes. First without the developer option "Don't keep activities":

  • ActivityA is the root activity
  • We start ActivityA
  • ActivityA.onCreate() is called
  • ActivityA now starts ActivityB
  • ActivityB.onCreate() is called (The activity stack now contains ActivityA->ActivityB)
  • ActivityB starts ActivityA with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP and an extra "foo"
  • ActivityA.onNewIntent() gets called with the Intent containing FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP and an extra "foo"
  • ActivityB.onDestroy() is called since the activity stack was cleared back to ActivityA

Now, let's do the exact same thing but enable the developer option "Don't keep activities" (I've highlighted in bold the stuff that is different from the previous scenario):

  • ActivityA is the root activity
  • We start ActivityA
  • ActivityA.onCreate() is called
  • ActivityA now starts ActivityB
  • ActivityB.onCreate() is called (The activity stack now contains ActivityA->ActivityB)
  • Because ActivityA has stopped, Android destroys it and calls ActivityA.onDestroy()
  • Note: The activity stack still contains ActivityA->ActivityB, even though there is no instance of ActivityA at the moment. Android remembers all the state
  • ActivityB starts ActivityA with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP and an extra "foo"
  • Since Android has no instance of ActivityA to reactivate, it needs to create one, so it does and then...
  • ActivityA.onCreate() is called with the same Intent that it was called with when the original instance of ActivityA was created (ie: LAUNCH intent with no flags and no extras)
  • ActivityA.onNewIntent() gets called with the Intent containing FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP and an extra "foo"
  • ActivityB.onDestroy() is called since the activity stack was cleared back to ActivityA

The important thing to note here is that Android always calls onCreate() whenever it creates an activity instance. Think of it like the constructor of an Activity. If Android has to recreate an instance of an Activity because the process was killed or the activity was destroyed, then it will instantiate a new object, then call onCreate() and then (if necessary) it will call onNewIntent().

When you call setIntent() this doesn't actually change the Intent that Android saves and restores. That only changes the in-memory Intent that will be returned from a call to getIntent().

I hope this is clearer now. If not, please let me know.



回答2:

Not sure if you've found a solution to this or not, but overriding the onNewIntent(Intent theNewIntent) method for the target activity & calling setIntent(theNewIntent) solved it for me.

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);

    /*
     * This overrides the original intent.
     */      
    setIntent(intent);
}