FLAG_ACTIVITY_NEW_TASK clarification needed
Android has TONS of bugs related to activities and tasks.
Nevertheless, Google changed the behavior of tasks between OS versions and didn't notify the developers, which is the most annoying thing about it.
jakk - If you didn't set any flags on the activities (A or B), than the behavior you are describing is WRONG.
And for all the ones which say that there is no problem with the documentation, try this:
- Create an application with Activity A (launching activity) & B (with the default launch mode for both).
- Start the application - a task is created with activity A only.
- From a button in activity A, launch activity B with a FLAG_ACTIVITY_NEW_TASK.
- Click the button several times and you'll see that activity B is created multiple times inside the task, which is NOT as the documentation says.
There are more scenarios to prove that the documentation is BAD / WRONG.
Android Notification: Intent.FLAG_ACTIVITY_NEW_TASK required?
Technically this flag is required. But since it is required, Android is nice and will just set it for you ;-)
The reason it is required is as follows:
The code that processes the Notification
and calls startActivity()
to actually launch the Intent
, isn't running in a "task". It is system code, part of the Notification
system. Normally, if you call startActivity()
and the flag Intent.FLAG_ACTIVITY_NEW_TASK
is not set, Android will try to launch that Activity into the current task (ie: the task containing the Activity that is calling startActivity()
). Since, in this case, there is no task, Android must launch the Activity into another task. That's why you need to specify Intent.FLAG_ACTIVITY_NEW_TASK
.
In practice, this won't always create a new task, since Android will first try to find an (suitable) existing task to launch the Activity into. If your app is already running, Android will just launch the Activity into that task. (This isn't 100% true, there are other special cases that can change this logic, but I'm not going to address them here).
NOTE: The same situation exists if you call startActivity()
from a Service
or a BroadcastReceiver
. In those cases, the flag Intent.FLAG_ACTIVITY_NEW_TASK
must be set, because there is no "current task", so Android must launch the Activity into another task.
FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TASK acts weird
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.
Android - use of FLAG_ACTIVITY_NEW_TASK
Remember that when you click the Notification
it is from that Context
that the intent is being launched. That context doesn't have the Activity on it's task (infact, it will be a blank task).
What this results in is two version of the same Activity
(although still only one instance of you Application
) running. Each Activity
is running a different Task
.
If you don't need duplicate Activities of the same type in any of your stacks you could use the answer here:
https://stackoverflow.com/a/2327027/726954
Otherwise, there are many ways to "fix" this problem, including singleton variables and Application Context methods that keeps track of which Activities are in a Running state.
You may need to search and refine your question for those.
Why use both Intent.FLAG_ACTIVITY_NEW_TASK and Intent.FLAG_ACTIVITY_SINGLE_TOP?
AFAIK
FLAG_ACTIVITY_SINGLE_TOP
doesn't create a new task to launch itself. It always relies on the previous activity's task to launch itself (along with this, it does check if there is an instance already in the stack and show that if available. In either way, it doesn't create a new task)
where as
FLAG_ACTIVITY_NEW_TASK
does create a new task unless there is a task with the same activity.
I know it kinda feels the same but the key difference here is when an activity is launched with FLAG_ACTIVITY_SINGLE_TOP
for the FIRST TIME, it relies on existing task and FLAG_ACTIVITY_NEW_TASK
creates a new task
What Are the Differences Between FLAG_ACTIVITY_RESET_TASK_IF_NEEDED and FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP?
I had a look at the source code for the ActivityManager
. The flag Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
does indeed do some magic that Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP
does not do: It triggers task reparenting.
Here's an (albeit lame) example:
In App A we have the root Activity RootA
and we have another Activity ReparentableA
:
<application
android:label="@string/app_name">
<activity android:name=".RootA">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ReparentableA"
android:allowTaskReparenting="true"/>
</application>
App A has the package name "com.app.a" so the default taskAffinity
of its components is "com.app.a".
In App B we have the root Activity RootB
:
<application
android:label="@string/app_name">
<activity android:name="RootB">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
App B has the package name "com.app.b" so the default taskAffinity
of its components is "com.app.b".
Now we launch App B from the HOME screen. This starts a new task and creates a new instance of Activity RootB
as the root Activity in that task. Activity RootB
now launches Activity ReparentableA
in the standard way, without any special flags. An instance of ReparentableA
is created and put on top of RootB
in the current task.
Press HOME.
Now we launch App A from the HOME screen. This starts a new task and creates a new instance of Activity RootA
as the root Activity in that task. NOTE: When Android launches a "launcher" Intent, it automatically sets the flags Intent.FLAG_ACTIVITY_NEW_TASK
and Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
. Because of this, launching RootA
now triggers task reparenting. Android looks to see if there are any activities in any other tasks that have an affinity for this new task (and are reparentable). It finds ReparentableA
(which has the same task affinity as RootA
) in the App B task and moves it to the new App A task. When launching App A we do not see RootA
, we actually see ReparentableA
, as it is moved to the top of the new task.
If we return to App B, we can see that ReparentableA
is gone from the task stack and that task now consists of only one Activity: RootB
.
Notes about using Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP
The important thing to remember about using these flags to "reset a task" is that it only works if there is already an instance of the target Activity at the root of the task. If your root Activity ever finishes, you cannot clear your task by starting the root Activity with Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP
. Android will just create a new instance of the target (root) Activity and put it on top of the existing activities in the task, which is probably not at all what you want.
Difference between Intent.FLAG_ACTIVITY_CLEAR_TASK
and Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP
:
As noted above, using CLEAR_TOP | SINGLE_TOP
only works if there is already an instance of the target Activity in the task. CLEAR_TASK
, however, removes all activities from the task, regardless of whether or not there was an instance of the target Activity in the task. Also, using CLEAR_TASK
ensures that the target Activity becomes the root Activity of the task, without you needing to know what Activity was the root Activity before you cleared the task.
Difference between Intent.FLAG_ACTIVITY_CLEAR_TASK
and Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
:
As indicated above, using CLEAR_TASK
will always remove all activities from the task and launch a new instance of the target activity. In contrast, RESET_TASK_IF_NEEDED
will only reset the task in certain situations (the "IF_NEEDED" part). The task is only "reset" if Android is either:
- Creating a new task (in which case the "reset" functionality involves the task reparenting explained above), or
- If Android is bringing a background task to the foreground (in which case the task is only cleared of any activities that were launched with
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
and any activities that are on top of those activities). NOTE: The root Activity is never cleared in this case.
IMPORTANT NOTE: When you are testing, please note that there is a difference in the way Android behaves when launching apps from the HOME screen (or from the list of available applications) and when selecting tasks from the recent task list.
In the first case (launching an app by selecting it from the list of available applications or from a shortcut on the HOME screen), a launcher Intent
with Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
is created. This is used regardless of whether or not the app is already running. The Intent
is launched and then the ActivityManager
figures out what to do.
In the second case (selecting a task from the list of recent tasks), if the task still exists, it is just brought the front. The task "reset" is NOT performed if the task is just brought to the front using the recent task list. It isn't obvious to me how this is managed and I've not had a chance to look through the source code to figure out why that is.
I hope this answers your questions. Looking forward to your feedback and test results.
Related Topics
Why Does Flag_Activity_Clear_Top Not Work
How to Create an Android Activity and Service That Use Separate Processes
JSONarray Cannot Be Converted to JSONobject Error
Mediaplayer Stop Playing After About 5 Seconds
Disabling Android's Chrome Pull-Down-To-Refresh Feature
CSS Media Queries on Nexus 7, Display Resolution Not Working in Code
How to Setup Alertbox from Broadcastreceiver
How to Replace a Fragment on Button Click of That Fragment
Android Studio - Cannot Resolve Symbol 'Firebase'
How to Display List of Resource Drawables
Gradle Dsl Method Not Found: 'Implementation()'
How to Get Raw Preview Data from Camera Object at Least 15 Frames Per Second in Android
Play Youtube HTML5 Embedded Video in Android Webview
How to Avoid Scientific Notation in Double
Trying to Fix Networkonmainthreadexception But Gives Toast Error