Android "single top" launch mode and onNewIntent method

69,881

Solution 1

Did you check if onDestroy() was called as well? That's probably why onCreate() gets invoked every time instead of onNewIntent(), which would only be called if the activity is already existing.

For example if you leave your activity via the BACK-button it gets destroyed by default. But if you go up higher on the activity stack into other activities and from there call your ArtistActivity.class again it will skip onCreate() and go directly to onNewIntent(), because the activity has already been created and since you defined it as singleTop Android won't create a new instance of it, but take the one that is already lying around.

What I do to see what's going on I implement dummy functions for all the different states of each activity so I always now what's going on:

@Override
public void onDestroy() {
    Log.i(TAG, "onDestroy()");
    super.onDestroy();
}

Same for onRestart(), onStart(), onResume(), onPause(), onDestroy()

If the above (BACK-button) wasn't your problem, implementing these dummies will at least help you debugging it a bit better.

Solution 2

The accepted answer is not quite correct. If onDestroy() was called previously, then yes, onCreate() would always be called. However, this statement is wrong: "If you go up higher on the activity stack into other activities and from there call your ArtistActivity.class again it will skip onCreate() and go directly to onNewIntent()"

The "singleTop" section of http://developer.android.com/guide/components/tasks-and-back-stack.html explains plainly how it works (attention to bold text below; I've also proven this through my own debugging):

"For example, suppose a task's back stack consists of root activity A with activities B, C, and D on top (the stack is A-B-C-D; D is on top). An intent arrives for an activity of type D. If D has the default "standard"launch mode, a new instance of the class is launched and the stack becomes A-B-C-D-D. However, if D's launch mode is "singleTop", the existing instance of D receives the intent through onNewIntent(), because it's at the top of the stack—the stack remains A-B-C-D. However, if an intent arrives for an activity of type B, then a new instance of B is added to the stack, even if its launch mode is "singleTop"."

In other words, starting an activity through SINGLE_TOP will only reuse the existing activity if it is already at the top of the stack. It won't work if another activity in that same task is at the top (for example, the activity that is executing startActivity(SINGLE_TOP)); a new instance will be created instead.

Here are two ways to fix this so that you get the SINGLE_TOP behavior that you want -- the general purpose of which is to reuse an existing activity, instead of creating a new one...

First way (as described in the comment section of the accepted answer): You could add a launchMode of "singleTask" to your Activity. This would force onNewIntent() because singleTask means there can only be ONE instance of a particular activity in a given task. This is a somewhat hacky solution though because if your app needs multiple instances of that activity in a particular situation (like I do for my project), you're screwed.

Second way (better): Instead of FLAG_ACTIVITY_SINGLE_TOP, use FLAG_ACTIVITY_REORDER_TO_FRONT. This will reuse the existing activity instance by moving it to the top of the stack (onNewIntent() will be called as expected).

The main purpose of FLAG_ACTIVITY_SINGLE_TOP is to prevent the creation of multiple instances of an Activity. For instance, when that activity can be launched via an intent that comes from outside of your application's main task. For internal switching between activities in my app, I've found that FLAG_ACTIVITY_REORDER_TO_FRONT is generally what I want instead.

Solution 3

Set this flag to your intent:

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP)
Share:
69,881

Related videos on Youtube

Rich
Author by

Rich

I work here Tinder I used to work here Flipagram Trello Urbanspoon / Zomato Raizlabs Infusion TradeStation

Updated on August 18, 2020

Comments

  • Rich
    Rich over 3 years

    I read in the Android documentation that by setting my Activity's launchMode property to singleTop OR by adding the FLAG_ACTIVITY_SINGLE_TOP flag to my Intent, that calling startActivity(intent) would reuse a single Activity instance and give me the Intent in the onNewIntent callback. I did both of these things, and onNewIntent never fires and onCreate fires every time. The docs also say that this.getIntent() returns the intent that was first passed to the Activity when it was first created. In onCreate I'm calling getIntent and I'm getting a new one every time (I'm creating the intent object in another activity and adding an extra to it...this extra should be the same every time if it was returning me the same intent object). All this leads me to believe that my activity is not acting like a "single top", and I don't understand why.

    To add some background in case I'm simply missing a required step, here's my Activity declaration in the manifest and the code I'm using to launch the activity. The Activity itself doesn't do anything worth mentioning in regards to this:

    in AndroidManifest.xml:

        <activity
            android:name=".ArtistActivity"
            android:label="Artist"
            android:launchMode="singleTop">
        </activity>     
    

    in my calling Activity:

            Intent i = new Intent();
            i.putExtra(EXTRA_KEY_ARTIST, id);
            i.setClass(this, ArtistActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
            startActivity(i);
    
  • Rich
    Rich over 14 years
    that is most likely what's going on. I created something like a "landing page" activity as the entry point. From the landing point, you select an artist and go to the ArtistActivity. The user uses the back button to go back to the main activity and select again. I was under the impression that the Activity would hang around after first use and that I'd save on performance by not re-instantiating it. Should I worry about performance here, or is this pattern fine for simple activities?
  • kellogs
    kellogs over 12 years
    Hi, I have the same problem as OP, but onDestroy never gets called in my case - I just end up with brand new instances of the activity :-s. Any other ideas ?
  • kellogs
    kellogs over 12 years
    android:launchMode="singleTask" - in my case the ACtivity in question was getting started from outside its main task
  • AZ13
    AZ13 over 12 years
    @kellogs great tip. I'm working on a NFC-based app and every time a touched the phone with the tag, a new instance of my activity was started. The Jump-Back-Hierarchy was a mess. launchmode="singleTask" helps lot, the runtime enironment prevents instantiating multiple time the same activity, which helped me a lot! Thx.
  • jowett
    jowett over 12 years
    i see that ondestory() didn't be called. Only onStop() is called everytime. In fact, it is more likely enter directly to onResume(), neither onCreate(), nor onNewintent();
  • Kevin
    Kevin over 8 years
    singleTask instead of singleTop fixed the issues I was having on Samsung devices only - emulator and other devices worked just fine. Samsung did not. singleTask fixed that issue.
  • Steve B
    Steve B over 8 years
    singleTask limits you to only one instance of an activity in your task, which could conflict with how you want your application to behave in other use cases. Using FLAG_ACTIVITY_REORDER_TO_FRONT would likely be a better choice, as it reuses an existing activity by bringing it to the front of the stack.
  • GuillermoMP
    GuillermoMP about 8 years
    Thank you. Indeed the accepted answer explanation is right for "singleTask" behaviour, but not for "singleTop" where up navigation can create several instances of one activity.
  • yat0
    yat0 about 8 years
    One thing.. In the second way, using FLAG_ACTIVITY_REORDER_TO_FRONT should still be combined with the the launchMode "singleTop" right?