Hopefully someone can help me figure out, if not a solution, at least an explanation for a behaviour.
The Problem:
On some devices, pressing the launcher icon results in the current task being resumed, on others it results in the initial launch intent being fired (effectively restarting the app). Why does this happen?
The Detail:
When you press the "Launcher Icon" the app starts normally - That is, I assume, an Intent is launched with the name of your first Activity
with the action android.intent.action.MAIN
and the category android.intent.category.LAUNCHER
. This can't always be the case however:
On the majority of devices, if you press the launcher icon after the app is already running, the currently running Activity in that process is resumed (NOT the initial Activity
). It resumes in the same way as if you had selected it from the "Recent Tasks" in the OS menu. This is the behaviour I want on all devices.
However, on selected other devices different behaviour occurs:
On the Motorola Xoom, when you press the launcher icon, the App will always start the initial launch Activity
regardless of what is currently running. I assume that the launcher icons always start the "LAUNCHER" intent.
On the Samsung Tab 2, when you press the launcher icon, if you have just installed the app, it will always launch the initial Activity
(Same as the Xoom) - however, after you restart the device after the install, the launcher icon will instead resume the app. I assume that these devices add "installed apps" into a lookup table on device startup which allow the launcher icons to correctly resume running tasks?
I've read many answer that sound similar to my problem but simply adding android:alwaysRetainTaskState="true"
or using launchMode="singleTop"
to the Activity
are not the answer.
Edit:
After the most recent launch of this app, we find that this behaviour has begun to occur on all devices after the first restart. Which seems crazy to me but looking through the restart process, I can't actually find what's going wrong.
Aha! (tldr; See the statements in bold at the bottom)
I've found the problem... I think.
So, I'll start off with a supposition. When you press the launcher, it either starts the default Activity
or, if a Task
started by a previous launch is open, it brings it to the front. Put another way - If at any stage in your navigation you create a new Task
and finish
the old one, the launcher will now no longer resume your app.
If that supposition is true, I'm pretty sure that should be a bug, given that each Task
is in the same process and is just as valid a resume candidate as the first one created?
My problem then, was fixed by removing these flags from a couple of Intents
:
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK );
While it's quite obvious the FLAG_ACTIVITY_NEW_TASK
creates a new Task
, I didn't appreciate that the above supposition was in effect. I did consider this a culprit and removed it to test and I was still having a problem so I dismissed it. However, I still had the below conditions:
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
My splash screen was starting the "main" Activity
in my app using the above flag. Afterall, If I had "restart" my app and the Activity
was still running, I would much rather preserve it's state information.
You'll notice in the documentation it makes no mention of starting a new Task
:
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.
So, I had the situation as described below:
A
launched B
with FLAG_ACTIVITY_CLEAR_TOP
, A
finishes. B
wishes to restart a service so sends the user to A
which has the service restart logic
and UI (No flags).A
launches B
with FLAG_ACTIVITY_CLEAR_TOP, A
finishes.At this stage the second FLAG_ACTIVITY_CLEAR_TOP
flag is restarting B
which is in the task stack. I'm assuming this must destroy the Task
and start a new one, causing my problem, which is a very difficult situation to spot if you ask me!
So, if all of my supposition are correct:
Launcher
only resumes the initially created TaskFLAG_ACTIVITY_CLEAR_TOP
will, if it restarts the only remaining Activity
, also recreate a new Task