I've been testing the intent flags but I need to clarify something. I have two items in my navigation drawer and on click I do this,
Intent intent = new Intent(this, activityClazz);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
which classes are activities with same navigation drawers. -No fragment is used for various reasons-. In those activities I have buttons which open same activities but without the intent flags. What I wanted to do is to navigate activities with default Android behavior but also stack them in different stacks according to navigation items, like a tab usage.
Test case:
startActivity without the clear-new flag
startActivity with the clear-new flag
back
I expect to return to the first activity since I started the second one with a new task, so the first one should have stayed in the first stack) but I found out that the first one already destroyed.
The first activity (the one started without the flags) is destroyed because the flag combination for your second activity does the following:
FLAG_ACTIVITY_NEW_TASK: Start the activity in a new task OR if the activity already exists bring its task to the foreground. In our case, it does not exist yet. If you would only use this flag, then you would have task1 with activity1 and task2 with activity two. If you not hit the back button, task2 and activity 2 are dismissed and you return back to task1 and activity1.
FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TASK: The clear task flag now enforces that if a task is brought to the front by new task then it is first cleaned (activities are finished). Citing the documentation:
If set in an Intent passed to Context.startActivity(), this flag will cause any existing task that would be associated with the activity to be cleared before the activity is started. That is, the activity becomes the new root of an otherwise empty task, and any old activities are finished. This can only be used in conjunction with FLAG_ACTIVITY_NEW_TASK.
In conjunction, this means that with the back button you bring task1 with activity1 to the front, BUT the clear flag immediately finishes activity1. So this is why you encounter activity1 as being finished already.
Related
I have 8 Activities in my Android app and I want:
1)Every time I press Back button during my first 7 Activities to go back to my previous Activity(Act1< Act2< Act3< Act4< Act5< Act6< Act7) BUT
2)ONLY when I am in the 8th Activity I want to definitely exit my Android app and go to my phone's Home Screen.I try to do it by overriding onBackPressed method in my 8th Activity (Phone Home Screen<-Act8)
I found an Android implementation in which I insert finish();in every intent of all my 8 Activities but this is not what I want since this way I can't go back to the previous Activity whenever I want(with finish(); every current Activity is removed from back stack).
How will I do it please?
My code so far in my 8th Activity is:
#Override
public void onBackPressed()
{
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
Another way: create a 9th Activity and call it FinishAllActivity or something like that. Make this activity call finish() and then return in its onCreate().
In onBackPressed() in Activity 8, start FinishAllActivity using the FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK flags (see this question for more details). Activities 1-8 will be removed from the stack, then the 9th Activity will start and immediately terminate and your task stack is clear. When you reopen the app it should start from Activity 1.
The advantage of doing it this way is that you don't have to modify Activities 1-7.
Add a public static boolean to one of your classes that indicates the app is exiting. Set this boolean in activity 8 when you want the app to finish, and have all of your other activities check it in their onResume() and finish immediately if it is true. Make sure the first activity clears it before finishing, or it may still be set the next time the app runs. (Android doesn't necessarily discard the VM when your last activity finishes, so the class and its static members may be reused next time.)
Note that this is the simple way, not the "Android way." Global variables are generally frowned upon, for reasons you can Google. The "correct" way to do this would be to start each activity for result and return a result to onActivityResult(...) that indicates whether the app is exiting.
You can implement a broadcast receiver and have each of your Activities that you want to close call finish() when they receive the broadcast (which will be sent from your last activity). I would imagine you'd need to have your broadcast receiver class be either an anonymous inner class or a private class within your activity(s) so that you can easily access your enclosing Activity's finish method.
Here's a good example of broadcast receivers:
http://www.tutorialspoint.com/android/android_broadcast_receivers.htm
Look at the custom intents section.
Doing it this way is a loosely coupled way to implement what you are looking to do.
use FLAG_ACTIVITY_CLEAR_TOP Flag in your intent like below example.
Intent intent = new Intent(getApplicationContext(),FirstActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("EXIT", true);
startActivity(intent);
in your first activity check below condition.
if (getIntent().getBooleanExtra("EXIT", false)) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
finish();
}
here FLAG_ACTIVITY_CLEAR_TOP work like below example
consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B.
so here you have to call D is your last activity and A is your first activity.
This way you are finishing your 8th Activity returning to your 7th Activity and same time you are like emulating a pressing of Home button on a device. When you rerun your app it will appear with 7th Activity on a screen. If you wish to see the 8th Activity in this case then just remove the finish() method. If you wish next time your app to start with 1st Activity then you should finish all the activities from 8 to 2nd but not the 1st. Also you could launch your 1st Activity adding a FLAG NEW_TASK or some other flags.
UPDATE
My advise (for the quick result without changing the workflow) is to use startActivityForResult() to start all activities in chain. When user exits the app just return a special parameter using setActivityResult() to get all nested activities know about user's choice making all nested Activities to run finish(). This way all your 8 activities will be finished properly.
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 to launch an external activity (which isn't mine) for a split second, and then I have a service that launches my previous activity for the user to see.
This works great, but the problem is when the user then presses the back key, he gets to that external activity.
I have two different activity launches here, one for the external activity, and one for my activity to return to (in which I need to use FLAG_ACTIVITY_NEW_TASK because I'm launching it from a service).
For these two launches I've tried any combination of:
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, FLAG_ACTIVITY_NO_HISTORY, FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_CLEAR_TOP, FLAG_ACTIVITY_CLEAR_TASK, FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS.
Nothing helps, I keep seeing the external activity after pressing back.
I've seen some similar questions here, but they all have the middle activity in their own app, so they can call finish on it, or set noHistory="true" in the manifest.
Any ideas?
UPDATE:
Seems like the issue is that I re-launch my previous activity from a service, which means I need to give it FLAG_ACTIVITY_NEW_TASK, from the documentation of Tasks and Back Stack:
FLAG_ACTIVITY_NEW_TASK Start the activity in a new task. If a task is
already running for the activity you are now starting, that task is
brought to the foreground with its last state restored and the
activity receives the new intent in onNewIntent(). This produces the
same behavior as the "singleTask" launchMode value, discussed in the
previous section.
And from the singleTask paragraph:
"singleTask" The system creates a new task and instantiates the activity at the root of the new task
...
Note: Although the activity starts in a
new task, the Back button still returns the user to the previous
activity.
So I need a flag that overrides this behavior somehow...
Here's what I want to do:
If I use startActivity I start a new Activity and I can't use StartActivityForResult from Main Menu -> Total. I would like to go back on the Main Menu activity already present in my stack and remove Activities A, B ,C and Total.
Have you tried using FLAG_ACTIVITY_CLEAR_TOP flag in your intent (used to start "Main Menu" from "Total")?
http://developer.android.com/reference/android/content/Intent.html#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.
To remove activities A,B,C, and Total from your stack, just call finish() on these 4 activites.
See also : Tasks & Back stacks and Activity task Design
In my application you can navigate through several Activities until the Activity stack is quite deep.
We'd like a button on every Activity that will take you straight back to the main menu - i.e. pop all Activities from the stack except the first one.
I've put the button in a View that I can easily put on every Activity in the application, but I can't figure out how to close several Activities in one fell swoop.
(If possible, it would be good if the View could work out how many Activities to close by itself - i.e. detect how deep on the stack its own Activity is.)
Have a look at the intent flag FLAG_ACTIVITY_CLEAR_TOP which says it brings the targeted activity to the top of the stack, removing everything else that might have been above it. So use that button you can add to all your activities to launch an intent which targets your main menu, with that flag set.
From the documentation:
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.
For example, consider a task
consisting of the activities: A, B, C,
D. If D calls startActivity() with an
Intent that resolves to the component
of activity B, then C and D will be
finished and B receive the given
Intent, resulting in the stack now
being: A, B.
The currently running instance of
activity B in the above example will
either receive the new intent you are
starting here in its onNewIntent()
method, or be itself finished and
restarted with the new intent. 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; for all other
launch modes or if
FLAG_ACTIVITY_SINGLE_TOP is set then
this Intent will be delivered to the
current instance's onNewIntent().
This launch mode can also be used to
good effect in conjunction with
FLAG_ACTIVITY_NEW_TASK: if used to
start the root activity of a task, it
will bring any currently running
instance of that task to the
foreground, and then clear it to its
root state. This is especially useful,
for example, when launching an
activity from the notification
manager.
You could declare that first activity android:launchMode="singleTask" (more) and then just start it with an Intent.
EDIT: My suggestion is based on the assumption that you want to have a single instance of the Activity to return to. Otherwise it's incorrect.