Android default launchMode of LAUNCHER activity? - android

does the launchMode of the launcher activity in the manifest get ignored?
The android documentation says that the default launchMode is "standard" but this isn't logic for me if this would be applied to the main activity of an app because each time you start the app, another task would be created in the instance of the app.

Well, I delved into Android sources myself and found the following thing.
The launcher starts apps using the method startActivityAsUser in LauncherAppsService. The intent is constructed using these lines:
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
launchIntent.setComponent(component);
launchIntent.setSourceBounds(sourceBounds);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
According to Android documentation, the flag FLAG_ACTIVITY_NEW_TASK means:
When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in.
This effectively and unconditionally overrides launchMode specified (or omitted to default behaviour) in the app, and ignores this attribute.
I think this demonstrates that the documentation is not clear (or complete) enough. Without such deep investigations of the core source codes everyone can get unexpected results now and then.

You are confusing two things. One is launchMode and the other is "what happens when the user selects an app icon from the HOME screen, or selects a task from the list of recent tasks". These are 2 completely different things.
launchMode
Each Activity has a specified launchMode (the default is "standard" or "multiple". This tells Android how to start this Activity, and there are many factors that can contribute to the "interpretation" of the launchMode. It depends on what other flags may have been specified in the Intent used. It depends on which task requested the launch of the Activity (or if the launch was requested from a non-activity context, like from a Service or BroadcastReceiver). It depends on whether or not an existing instance of the Activity is already active in the specified task, etc.
Behaviour on selecting an app icon from the HOME screen or list of installed applications
When the user selects an app icon, startActivity() is called with an Intent containing the following data:
ACTION=MAIN
CATEGORY=LAUNCHER
Component is set to the package name and the class name of the Activity that is defined in the manifest with ACTION=MAIN and CATEGORY=LAUNCHER
Flag FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_RESET_TASK_IF_NEEDED are set.
Regardless of the launchMode definition of the Activity to be launched, calling startActivity() with an Intent like this causes the following behaviour:
If there is already an existing task whose task affinity matches the Activity being started (in simple terms, if the app is already running), Android will simply bring the existing task to the foreground. That's it. It doesn't create an instance of any Activity. It doesn't call onNewIntent() on any Activity. It does nothing other than bringing the existing task to the foreground. This is why, even if you specify launchMode="standard" for your launcher Activity, Android doesn't create a new instance every time you click on your app icon.
If there isn't already an existing task whose task affinity matches the Activity being started (in simple terms, if the app isn't already running), Android will create a new task and launch the Activity into that task. launchMode doesn't play a role here, since there is absolutely no difference between the launch modes when launching a single Activity into a new task. Android always creates a new task and always creates a new instance of the Activity as the root of that task.
This behaviour is also the same when the user selectsa task from the list of recent tasks. If the task is still running, Android just brings the task to the foreground, does not start any new Activity instances and does not call onNewIntent(). If the task is no longer running, Android creates a new task and launches the launcher Activity into that task. The only difference here is that the flag FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY is also set in the Intent if the user selected a task from the list of recent tasks.
I hope this answers your question.
See this answer for a very detailed explanation of FLAG_ACTIVITY_RESET_TASK_IF_NEEDED and task reparenting in general.

Think of everything except the opening activity as an abstract implementation. Declaring an activity as
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Will cause it to open first. Subsequent activities are Overriden at the time an Intent is formed to navigate between activities. The overrides are represented as intent flags.
A list of intent extras:
http://developer.android.com/reference/android/content/Intent.html
With flags being commands you'd otherwise have written in the Manifest.

You are right.The default mode is "standard".
According to android documentation
*In standard mode ,Every time there's a new intent for a "standard" activity, a new instance of the class is created to respond to that intent. Each instance handles a single intent.
*.If the parent activity has launch mode standard (and the up intent does not contain FLAG_ACTIVITY_CLEAR_TOP), the current activity and its parent are both popped off the stack, and a new instance of the parent activity is created to receive the navigation intent.

The behavior of Activity set to standard mode is a new Activity will always be created to work separately with each Intent sent. Imagine, if there are 10 Intents sent to compose an email, there should be 10 Activities launch to serve each Intent separately. As a result, there could be an unlimited number of this kind of Activity launched in a device.
Behavior on Android pre-Lollipop
standard Activity would be created and placed on top of stack in the same task as one that sent an Intent.
For example, when we share an image from gallery to a standard Activity, It will be stacked in the same task as described although they are from the different application.
If we switch the application to the another one and then switch back to Gallery, we will still see that standard launchMode place on top of Gallery's task. As a result, if we need to do anything with Gallery, we have to finish our job in that additional Activity first.
Behavior on Android Lollipop
If the Activities are from the same application, it will work just like on pre-Lollipop, stacked on top of the task.
But in case that an Intent is sent from a different application. New task will be created and the newly created Activity will be placed as a root Activity like below.
Source from here

Related

Activities opened with startActivityForResult not appearing in Recent Screen

I'm following this guide to show my activity in Recent Screen like a separate task.
I've added this flag to open my activity in a new task:
newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
When I start an activity using startActivityForResut the new activity is not opened as a separate task.
However, if I start an activity using startActivity it is opening in a new task.
Is this is the default behavior? or am I missing something?
See the documentation for FLAG_ACTIVITY_NEW_TASK:
This flag can not be used when the caller is requesting a result from the activity being launched.
So I guess if you're asking for a result, android forces you to stay in the same task.

Start new activity from notification in existing task

My app receives pushes and opens different activities according to the push type.
I use TaskStackBuilder for a pending Intent to create a synthetic backstack in conjunction with android:parentActivityNamein my manifest.
So far, so easy. When the App is not started, all works as expected. But if the app is in background (task is running), the pending Intent also starts my desired activity with the defined parent from the manifest, but resets the existing task. The problem is that other activities that were started by the user in the meantime are also cleared.
So what a want to achieve is:
if the app is not started, open the desired activity with the synthetic backstack (MainActivity)
if the app is running, respect the current task order and just push the desired activity on top of it.
I can't seem to make it work with the TaskStackBuilder.
I'd be happy for some insights.
You can't really do this with TaskStackBuilder. It isn't designed for that. It always resets the task to begin with.
I would do the following:
Have the Notification start an Activity. Don't use TaskStackBuilder and don't create any artificial back-stack. This Activity will run in the application's current task if the application is currently active, and it will be put on top of the most recent Activity that is open.
In onCreate() of this new Activity, check if this Activity is the root of the task using isTaskRoot(). If this Activity is the root of the task, this means that the app was not active prior to launching this Activity. In this case, you can create an artificial back-stack using TaskStackBuilder and launch the thing again the way you want it (this will reset the task).
Try using PendingIntent.getActivities with FLAG_ONE_SHOT, this way I was able to open stack of activities with correct navigation

Send data to activity with FLAG_ACTIVITY_NEW_TASK and taskAffinity

I have almost the same requirements as this excellent question:
Send data to activity with FLAG_ACTIVITY_REORDER_TO_FRONT
However, I'm using FLAG_ACTIVITY_NEW_TASK because each of my primary activities needs to run as a new task. In my testing onNewIntent isn't called in this case (even when activity already running). So how do I pass data to it?
I can get onNewIntent to be called, by setting launchMode to singleTask, however then I run in to this bug: Android: bug in launchMode="singleTask"? -> activity stack not preserved
I want to accomplish:
If no instance exists, create a new instance, as a new task, and pass it data. (this part already works, I can get the data in onCreate)
If an instance already exists, bring the task (entire task, not just root/one activity) it is in to the front, and:
If task currently has a child activity displayed, do nothing
If task doesn't have a child activity displayed (just root/main activity), process the passed data and update the UI
I have one task with a button, "Go to Event", when clicked it needs to open/start the Calendar task and highlight a specific event (but the Calendar will ignore that request if another activity is being displayed over the top of it).
Thanks to #qbix's comment, I found this answer:
Add Intent.FLAG_ACTIVITY_SINGLE_TOP -- which seems like it wouldn't be valid when combined with FLAG_ACTIVITY_NEW_TASK, but it works. It achieves all behaviors I listed above, and only fires onNewIntent if the activity is at the top of the stack, so I don't even have to worry about detecting that and ignoring.
More details here, even though this Q&A is related to PendingIntent and notifications:
Intent from notification does not have extras

Setting launchMode="singleTask" vs setting activity launchMode="singleTop"

I have an app that is very hierarchical (activities are similar to League > Team > Position > Player) and so I've made each activity singleTop in order to keep navigation sensible and to prevent duplicate instances.
Now I'm making my second app and I've seen it suggested to declare my application to be singleTask to prevent duplicate instances. Could someone help explain the advantages of each approach?
My new app is just an activity with 3 fragments and then I'll probably add a settings activity and maybe a FAQ.
EDIT: I just realized that singleTask is NOT preventing duplicate instances of my app, as I had thought. Now looking for the right way to handle this...
I think your definition of singleTop and singleTask is a little off. SingleTop could produce a duplicate instance. Lets use your example, League > Team > Position > Player. If there is a button in the player screen that will take you to the league screen, it will become League > Team > Position > Player > League.
Whereas singleTask guarantees that only one instance of the activity can exist.
Android activity launchMode
4 modes...
"standard"
"singleTop"
"singleTask"
"singleInstance"
The default mode is "standard".
The modes fall into two groups. standard and singleTop comes in one side and singleTask and singleInstance comes in another side.
The main difference between standard and singleTop is in standard, every time a new intent for standard activity, a new instance is created. In case of singleTop too, a new instance is created but an instance of the activity is already in top of the stack, it wont create a new instance.
Actually, the issue comes , when we download an application from a server and launch it and open it from there itself. After launching the application, press home button. Then click the all programs and select the icon of the application from home screen. Then another activity will be created in the case of standard, but in singleTop , no new instance will be created.
The "singleTask" and "singleInstance" modes also differ from each other in only one respect:
A "singleTask" activity allows other activities to be part of its task. It's at the root of the activity stack, but other activities (necessarily "standard" and "singleTop" activities) can be launched into the same task.
A "singleInstance" activity, on the other hand, permits no other activities to be part of its task. It's the only activity in the task. If it starts another activity, that activity is assigned to a different task — as if FLAG_ACTIVITY_NEW_TASK was in the intent.
http://smartandroidians.blogspot.in/2010/04/activity-launch-mode-in-android.html
I found the answer here:
http://www.intridea.com/blog/2011/6/16/android-understanding-activity-launchmode
"singleTop":
The difference from 'standard' is, if an instance of activity already exists at the top of the current task and system routes intent to this activity, no new instance will be created because it will fire off an onNewIntent() method instead of creating a new object. Let's take the Twitter-oauth integration as example.
"singleTask":
A new task will always be created and a new instance will be pushed to the task as the root one. However, if any activity instance exists in any tasks, the system routes the intent to that activity instance through the onNewIntent() method call. In this mode, activity instances can be pushed to the same task. And if the user clicks the BACK key from the singleTask activity, the system will return the user to the previous activity.
From Understanding Activity launch mode:
standard (default) :- Multiple instances of the activity class can be
instantiated and multiple instances can be added to the same task or
different tasks. This is the common mode for most of the activities.
singleTop :- The difference from standard is, if an instance of the
activity already exists at the top of the current task and the system
routes the intent to this activity, no new instance will be created
because it will fire off an onNewIntent() method instead of creating a
new object.
singleTask:- A new task will always be created and a new instance will
be pushed to the task as the root. However, if any activity instance
exists in any tasks, the system routes the intent to that activity
instance through the onNewIntent() method call. In this mode, activity
instances can be pushed to the same task. This mode is useful for
activities that act as the entry points.
singleInstance:- Same as singleTask, except that the no activities
instance can be pushed into the same task of the singleInstance’s.
Accordingly, the activity with launch mode is always in a single
activity instance task. This is a very specialized mode and should
only be used in applications that are implemented entirely as one
activity.

Implications of setting a flag on the Intent

I'm calling context.startActivity(intent) from within a seperate OnClickListener class. In order for this to work I had to set the FLAG_ACTIVITY_NEW_TASK on the intent.
That all works as expected, but I'm wondering if there are any implications to doing this that I'm not aware of. Will this create any problems in terms of performance? Does it reflect poor design on my part?
What do you think?
From the Android docs: "This flag is generally used by activities that want to present a "launcher" style behavior: they give the user a list of separate things that can be done, which otherwise run completely independently of the activity launching them."
More from the Android docs: "Note that if this method is being called from outside of an Activity Context, then the Intent must include the FLAG_ACTIVITY_NEW_TASK launch flag. This is because, without being started from an existing Activity, there is no existing task in which to place the new activity and thus it needs to be placed in its own separate task."
As you are starting a new activity each time and adding this to the stack, if you leave that activity and then start another with the onClickListener you might run the risk of starting another activity instead of resuming the previous activity. I think changing the flag to FLAG_ACTIVITY_RESET_TASK_IF_NEEDED should fix this.

Categories

Resources