I want to keep a single instance of every Activity I start in my application. The launchMode singleTask was an option but it is working for only one Activity.
I want
to start an Activity if there is no instance and it is called.
and if any other instance of that Activity is present already then
that instance will brought to front without creating a new instance
of that Activity.
This property will be applied to more than one Activity.
No Activity does guarantee that it will be always on the top of the history stack.
My work until now:
I got many suggestions which are not valid for my case, so I want to point these out so that no other person would give the same suggestion.
I have set the launchMode to singleTop and this works only if the Activity is at the top of the history stack. onNewIntent() only gets called if Activity is at the top of history stack. and in my case the Activity may be at any position in stack. So this is not working.
When you launch an Activity, do it like this:
Intent intent = new Intent(this, MyActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);
If an instance of this Activity already exists, then it will be moved to the front. If an instance does NOT exist, a new instance will be created.
You can set the android:launchMode of your activity to singleTop
In this case the if the activity already exists, new intents will bring it to front will be delivered to the activity's onNewIntent()
http://developer.android.com/guide/topics/manifest/activity-element.html#lmode
This will work if your activity is on the top of the stack.
if you want to have a single instance of the activity, then you can set your launchMode to singleTask, but this is not recommended as it will make your activity reside in a separate task , which can be confusing to the users.
Use singleTop launch mode instead (docs): if there already is an Activity instance with the same type at the top of stack in the caller Task, there would not be any new Activity created, instead an Intent will be sent to an existed Activity instance through onNewIntent() method.
See can also this article for details on launch modes.
Related
I launch an activity with startActivityForResult() and want to prevent multiple instances from being started at the top of activity stack. So I expect android:launchMode="singleTop" to do its work, but for some reason the flag gets ignored.
After some investigations I managed to launch only one instance by adding FLAG_ACTIVITY_REORDER_TO_FRONT to intent, but I would be grateful if someone could explain me the reason why "singleTop" doesn't work in such case. The code is very simple.
// Activity class
Intent intent = new Intent(this, DetailsActivity.class);
// multiple instances can be launched without this line
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivityForResult(intent, REQUEST_CODE_DETAILS);
// AndroidManifest.xml
<activity
android:name=".DetailsActivity"
android:launchMode="singleTop"/>
How SingleTop work?
let suppose you have current activity stack like
A->B->C
Now from current activity C, if you start A activity which is a singleTop, so in this case system will create a new instance of A and brings that instance to top. (If specified activity is not on top then new instance will be created)
So stack will look like
A->B->C->A
Now if you try to open A again then in this case as A on top already, so NO new instance will be created. Instead A will receive callback in onNewIntent() method
Flag
FLAG_ACTIVITY_REORDER_TO_FRONT
it scans from the front of stack to back of stack and if it found instance of specified activity then brings that activity to front.
So in your case if DetailsActivity instacne is already present in system then this flag will bring DetailsActivity to front
The flag FLAG_ACTIVITY_SINGLE_TOP must be ignored in this case because when you launch an Activity using startActivityForResult() you are basically saying that you want to launch an Activity and have the Activity return you a result.
If an instance of this Activity already exists in the stack (regardless of where it is in the stack), that instance has not been setup to return a result to your Activity. It is possible that that instance was launched using startActivity() in which case it isn't setup to return a result at all. It could be that it was launched using startActivityForResult() from another Activity, in which case it is set up to return a result to the Activity that launched it, not to your Activity.
These 2 things: FLAG_ACTIVITY_SINGLE_TOP and startActivityForResult() therefore are in conflict.
I am facing a strange issue
Activity A gets called and Fragment A has been attached to it.
Method gets called (onCreate, onStart,onResume) .
: I received the notification and I tab on that with intent for Activity A.
Blockquote
(intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
)
When Activity A is opened it call the below method.
(onPause,onCreate, onStart,onResume (Fragment detached) and than onDestroy)
Why on destroy is called for the old instance of the activity when I have already called CLEAR_TOP.
Can someone help me what can be the cause for this?
How can I check if there is any instance is pending in stack already or how can I clear everything?
I can not use singleInstance as on notification tab I am redirecting to different fragment.
If you use only FLAG_ACTIVITY_CLEAR_TOP then all instances of activities will be cleared, back to and including the instance of the target Activity, and then a new instance of the target Activity will be created.
If you want to reuse an existing instance, you need to specify both FLAG_ACTIVITY_CLEAR_TOP and FLAG_ACTIVITY_SINGLE_TOP like this:
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP);
This will clear all instances of activities back to (but excluding the target Activity, and call onNewIntent() on the target Activity with the new Intent.
I have created an activity , set it's filter as a Home activity and set launchMode with singleTask. When I press home ,the activity will be in onpause state then onStop . What confuse me is why the activity will be re-launched when back from icon displayed on "home pick" dialog ? It calls onCreate() again without invoked onDestroy().
I know there is a way to solve this problem that is to set launcherMode as singleIstance, but this way is not good enough, because it will cause an other issue that the activity will not be able to process onActivityResult.
You may want to tell what you wish to achieve. Because that's how singleTask is. It delivers existing intent to onNewIntent() of the existing instance of activity. So activity will not be destroyed but onCreate() will be called, as it will reCreate activity with existing intent.
Quoting the documentation
The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.
I've read in the android docs that singleTop mode is this:
If an instance of the activity already exists at the top of the
current task, the system routes the intent to that instance through a
call to its onNewIntent() method, rather than creating a new instance
of the activity. The activity can be instantiated multiple times, each
instance can belong to different tasks, and one task can have multiple
instances (but only if the activity at the top of the back stack is
not an existing instance of the activity).
However, my app is behaving differently. My main activity has the singleTop launch mode defined in the manifest file. Here is where it's behaving oddly.
Start main activity from launcher.
From main activity, start sub activity.
When user presses back button (or actionbar home button), it sends intent to main activity with some extras. This means that main activity needs to be updated (depending on the user actions in sub activity.)
Main activity is shown with updated display.
-- the odd part is this --
From main activity, pressing back button goes back again to main activity.
Pressing back button a 2nd time brings up the launcher screen, then my app is put in the background.
On step 5, why does it bring up the main activity again? I thought singleTop will bring to front the main activity which is the current top of the stack in the task. But from that behavior in #5 and #6, it seems like it's creating two instances of main activity instead.
Is my understanding incorrect or something else is going on that I'm not clear of yet. Please help explain/clarify. Thanks.
My sub activity has its onBackPressed method overridden. And likewise, the main activity onNewIntent() handles the extras.
#Override
public void onBackPressed() {
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra(MainActivity.UPDATE_ARG, true);
startActivity(intent);
super.onBackPressed();
}
Note: If I use singleTask mode, it behaves as I expected of singleTop. But I've read somewhere that singleTask and singleInstance are to be used sparingly.
From posted doc:
one task can have multiple instances (but only if the activity at the
top of the back stack is not an existing instance of the activity).
From your code:
When user presses back button (or actionbar home button), it sends
intent to main activity with some extras. This means that main
activity needs to be updated (depending on the user actions in sub
activity.)
when user presses back button your main activity dose not exist at the top of the current task and your sub activity is at the top because it has not destroyed yet, so it creates another main activity and do not use existing one because that main activity is not top.
look at this from the doc, note that the backstack contains current foreground activity:
why does it bring up the main activity again?
Well, because you're starting your activity again when you do:
startActivity(intent);
Any updates in your Activity should be performed in the onStart method (or onResume if applicable).
I've finally made sense out of all this (I think!). I'll explain.
android:launchMode attribute applies to the activity whether it's started from home launcher, from within your app (or from another app).
The intent flag Intent.FLAG_ACTIVITY_CLEAR_TOP is needed to implement the singleTop behavior. Therefore, I passed that flag when creating the intent prior to starting the activity.
For the main activity, here's what I did:
#Override
public void onBackPressed() {
Intent intent = new Intent(this, MainActivity.class);
// these flags are important!!
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(MainActivity.UPDATE_ARG, true);
startActivity(intent);
super.onBackPressed();
}
And correspondingly, I declared android:launchMode="singleTop" in the manifest file.
I need the calling activity to relaunch a child. that is, if the child activity already exist, close it then launch it again. and keeping the history/relationship between the parent and child. ie when I press back from the child I want it to go to the parent
CLEAR_TOP says that it will not relaunch an activity if it already exists
NEW_TASK sounds like it will make the child activity the root of the app which I don't want
According to my understanding you have 3 requirements:
1. There is only one instance of child activity:
I don't know the reason, but if you just want to ensure the singleton of child activity on current task top, then singleTop is fine; otherwise you need to use singleTask, but then child activity will be created at the root of another task.
2. If the child already exists, close it then launch it again:
For both singleTop and singleTask, if there is already an instance of child activity (for singleTop, already an instance on current task top), the intent will be delivered to that instance. You can reset your activity status in onNewInstance(). This should have the same effects of creating a new one.
3. When I press back from the child I want to go to the parent:
For singleTop, no need to make anymore effort.
For singleTask, you can try if this tricky works: If the child activity knows what is the next activity to show by pressing back, it can manually start the parent activity by calling startActivity() in onDestroy().
More information about Android lauchMode, read http://developer.android.com/guide/topics/manifest/activity-element.html#lmode
Use this, works for me
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_SINGLE_TOP|Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
getApplicationContext().startActivity(intent);