LaunchMode "singleTop" is ignored when activity is started with startActivityForResult - android

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.

Related

Keep only one instance of each activity in entire app

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.

Confused about singleTop mode

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.

Is there a way for a Android app to return directly to the activity it started with?

I'm just learning Android programming. The way I understand it is that the services work like a stack, Is there a way for a activity to return to the first activity that started the app, instead of just the previous one.
Example, say I have 4 activities, a,b,c,d. Is there a way for activity d to have a button that would bring up activity a?? Instead of activity d going to c, and c going back to b???
You can start again activity a, but using a flag in your intent:
FLAG_ACTIVITY_REORDER_TO_FRONT
From android docs:
If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought to the front of its task's history stack if it is already running.
Using this, you'll reuse the instance of activity a already running, instead of starting a new one. Note that doing this, the instances of activities b, c and d will remain in the back stack (now after activity a).
Otherwise, if you want to finish this activities (and remove them from the back stack) you can start activity a (from d) with this other flag:
FLAG_ACTIVITY_CLEAR_TOP
If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.
EDIT: this is a good resource to read, if you haven't did it yet:
Tasks and Back Stack.
Yes, you can do that. You simply code an intent for activity a to the button.
Use the command
finish():
You will be reverted to the main page
or else write an intent pointing to a
You can send information to different Activities via Intents.
Intent myIntent = new Intent(this, AvitivityName.class);
startActivity(myIntent);
Don't forget to add your Activities in AndroidManifest.xml.
Here's some tutorial on Android Intents:
how to use Android intents.
how to switch to another activity with a button click.

How to open already opened activity instead of creating new one?

I have two activities with "navigation menu" which has items for launching Activity1 and Activity2.
For example we starts Activity2 from Activity1 and then we want open Activity1 by tap on "navigation menu", but when we do this we get new instance of Activity1 instead of open еxisting instance.
How can i open instance of Activity1 if it already exists and create new instance if not?
Add FLAG_ACTIVITY_REORDER_TO_FRONT to your Intent you use with startActivity().
add android:launchMode="singleTop" to your activity in the Manifest.xml
<activity android:name=".myActivity" android:label="#string/app_name"
android:launchMode="singleTop" />
Check this out about different launchModes
also mind this:
As shown in the table above, standard is the default mode and is
appropriate for most types of activities. SingleTop is also a common
and useful launch mode for many types of activities. The other modes —
singleTask and singleInstance — are not appropriate for most
applications, since they result in an interaction model that is likely
to be unfamiliar to users and is very different from most other
applications
Set the flag of the activity to singleTask and override the onNewIntent(Intent intent) to catch the new intent.
The most complete answer would be to use android:launchMode="singleTask" and depending on your functionality, override onNewIntent since it will be called if there is already an instance of the Activity with the new Intent passed to it.
<activity
android:name=".MainActivity"
android:launchMode="singleTask"/>
Why?
Based on the question.
There are two Activities, Activity1 & Activity2
We open Activity1 and then from Activity1 we open Activity2. Then, inside Activity2:
How can i open instance of Activity1 if it already exists and create new instance if not?
As stated in AndroidManifestActivity_launchMode for singleTask
If, when starting the activity, there is already a task running that starts with this activity, then instead of starting a new instance the current task is brought to the front. The existing instance will receive a call to Activity.onNewIntent() with the...
Furthermore, under the intent class, if you read about the singleTask launchMode it already uses Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT and manually setting an intent to it is not normally set by application code.
As stated in FLAG_ACTIVITY_BROUGHT_TO_FRONT
int FLAG_ACTIVITY_BROUGHT_TO_FRONT
This flag is not normally set by application code, but set for you by the system as described in the launchMode documentation for the singleTask mode.
Therefore, by using singleTask launchMode you ensure that there is only one instance of your application and you do not have to be adding the FLAG_ACTIVITY_BROUGHT_TO_FRONT flag to your intents in every activity which calls your Activity2 as suggested by CommonsWare.
Now, if we use the android:launchMode="singleTop" as weakwire suggested, the link he provided himself clearly states;
"singleTop"...In other circumstances — for example, if an existing instance of the "singleTop" activity is in the target task, but not at the top of the stack, or if it's at the top of a stack, but not in the target task — a new instance would be created and pushed on the stack.
In other words, we would end up with multiple instances of Activity1 in some scenarios which is what we do not want.
As final say, if you are one of those who love editing answers that contribute nothing to the answer itself, go answer some real questions if you really want to increase your Stack Overflow Reputation.

Issue with activity called with Intent.FLAG_ACTIVITY_CLEAR_TOP

I want to finish my app calling the first activity with Intent.FLAG_ACTIVITY_CLEAR_TOP and finishing it. However, when it finishes, the app restarts automatically, and goes directly to Activity 2.
Why? Isn't the stack of activity supposed to be empty after finishing an activity called with Intent.FLAG_ACTIVITY_CLEAR_TOP?
My stack is Activity2>(more activities)>Activity1.
In Activity2
Intent exit_intent=new Intent(context, Activity1.class);
exit_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
exit_intent.putExtra("EXIT", true);
context.startActivity(exit_intent);
In Activity1
if (getIntent().getBooleanExtra("EXIT", false)) {
finish();
}
From the javadoc:
If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.
CLEAR_TOP will wipe out all Activities ABOVE Activity1: if Activity2 is below Activity1 then once Activity1 finishes you will see Activity2.
Are you sure finish() is being called properly in Activity1? I'm getting the feeling it's not, because:
I'm not sure where that if statement goes inside your Activity. It should be in onNewIntent.
If you are making that if statement inside the onNewIntent method, it's still wrong. The docs specify that getIntent() will always return the original intent that started the Activity, unless you call setIntent().
To conclude, maybe something else is getting called in your Activity1 (can't tell without the full code) that starts Activity2 instead of finishing.
If what I described is not the case, and your activity stack indeed looks like Activity2 > Activity1 like the others have described yes, it will not work. Just call finish() in Activity2?
According to the docs:
If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.
So it doesn't clear the entire activity stack, only any other activities that were on top of an old instance of the activity being launched.
You have it right but are you catching the intent inside onNewIntent method of your first activity? also activity 2 should be launched after activity 1 in order for this to work.
From Intent doc:
public static final int FLAG_ACTIVITY_CLEAR_TOP
If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created.

Categories

Resources