My app and activity is in my list of recent apps when I receive a notification. When I click on the notification, I want the intent of the notification to be honored. In my case I want to restart the activity (brute force) and pass in the new intent: so, finish then re-create. I am reading about such tags as FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_SINGLE_TOP but I don't understand them enough to know how to force a "finish then re-create` of my activity. And, oh, the activity in question is MainActivity.
The snippet inside GcmListenerService uses
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
for sending the notification intent
Alternatively
If I go with onNewIntent things get complicated because there maybe DialogFragments being displayed, etc. And I would have to clear everything. That is why finish then re-create seem like the simplest solution to me.
Intent flag FLAG_ACTIVITY_CLEAR_TOP should produce the desired behavior. The documentation for Tasks and Back Stack says this in the section for Using Intent Flags:
If the launch mode of the designated activity is "standard", it too is
removed from the stack and a new instance is launched in its place to
handle the incoming intent. That's because a new instance is always
created for a new intent when the launch mode is "standard".
The documentation for FLAG_ACTIVITY_CLEAR_TOP describes the same behavior in more detail.
Related
What is difference between using Intent flag "FLAG_ACTIVITY_NEW_TASK" & "FLAG_ACTIVITY_CLEAR_TOP" and launchMode="singleTask"? and What is the difference in setting intent flag as FLAG_ACTIVITY_SINGLE_TOP and setting launchMode to "singleTop".
I understood your question from your comment you replied to Tim. So you want to know the
behavior differences when setting launchModes and when setting
Intentflags to an activity
Answer to your this question is that you set launchMode for an Activity inside AndroidManifest.xml file but the particular launch behavior can be changed in some ways at runtime through the Intent flags FLAG_ACTIVITY_SINGLE_TOP, FLAG_ACTIVITY_NEW_TASK, and FLAG_ACTIVITY_MULTIPLE_TASK etc.
Now lets come to your two more questions that you mentioned in your main question.
What is difference between using Intent flag "FLAG_ACTIVITY_NEW_TASK"
& "FLAG_ACTIVITY_CLEAR_TOP" and launchMode="singleTask"?
FLAG_ACTIVITY_NEW_TASK
When we set this flag through intent and launch that activity. In that case that activity will become the start of a new task on this history stack. A task (from the activity that started it to the next task activity) defines an atomic group of activities that the user can move to. It means it will create a separate history stack. For example, in your app you have a settings icon, when you click on that you go to settings activity where you have further activities. Here all the actions recorded will start from your setting activity only.
FLAG_ACTIVITY_CLEAR_TOP
As its name is telling clearly, if you start an Activity with this flag in a existing task(please understand task then everything will be very easy to understand), in that case all the activities in stack above this activity will be closed and this will become the last activity or oldest activity in the Task.
singleTask
When you start an activity for which you set launchMode = "singleTask" and 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.
Your seconde question
What is the difference in setting intent flag as
FLAG_ACTIVITY_SINGLE_TOP and setting launchMode to "singleTop"?
FLAG_ACTIVITY_SINGLE_TOP and lanuchMode = "singleTop"
Both are have same behavior, flag is set at runtime and launchMode in AndroidManifest.xml in the beginning. The behavior is, activity this with this will be the only activity on the top in a Task. If it is already running at the top of the history stack then the activity will not be launched again.
NOTE: the best way to understand the behavior is to follow any tutorial and check it practically. Play around with code and see the behavior.
Here are some useful links:
Launch Modes : https://developer.android.com/reference/android/R.styleable#AndroidManifestActivity_launchMode
Intent flags : https://developer.android.com/reference/android/content/Intent
I think the Android documentation https://developer.android.com/reference/android/content/Intent#FLAG_ACTIVITY_CLEAR_TOP of FLAG_ACTIVITY_CLEAR_TOP explains the difference quite well. The launch modes are quite nicely explained at https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en.
What happens when you click on an app's launch icon?
Is a new intent always sent, or is the result sometimes the same as resuming a task from recent tasks?
If an intent is sent, when does it get sent to the onCreate() method of a new activity instance and when does it get routed through onNewIntent() of an existing activity?
Let's suppose the intent gets routed through onNewIntent() of an existing activity in the task. Which activity does it get sent to? The one nearest the top or the one nearest the root? Will it always get sent to an instance of the application's launch activity or can it sometimes get sent to an activity with the same affinity as the root? Can it ever get sent to an activity which does not share the same affinity as the root?
Finally, how is this all affected by the various launch modes (standard, single top, single instance, single task) of the activities in the task?
If there is anyone out there who understands all this, please help me!
What happens when you click on an app's launch icon?
Launcher apps calls startActivity with an intent [action = Intent.ACTION_MAIN, category = Intent.CATEGORY_LAUNCHER and flag = Intent.FLAG_ACTIVITY_NEW_TASK].
Regarding Intent.FLAG_ACTIVITY_NEW_TASK, from docs:
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.
onNewIntent basics:
onNewIntent is delivered only when activity has set either singleTask, singleInstance launch modes. It is also delivered if activity has set singleTop launch mode or the intent to start the activity has set the flag FLAG_ACTIVITY_SINGLE_TOP and the activity instance is already at the top of the target task. It means an attempt was made to launch a new instance of activity, instead the existing instance itself need to handle the intent.
Here is the response to your queries:
Is a new intent always sent, or is the result sometimes the same as
resuming a task from recent tasks?
If the task is already running, it is brought to foreground. In case FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET flag was used to launch a activity and latter the task is brought to foreground, then the activity is killed. From docs:
This is useful for cases where you have a logical break in your
application. For example, an e-mail application may have a command to
view an attachment, which launches an image view activity to display
it. This activity should be part of the e-mail application's task,
since it is a part of the task the user is involved in. However, if
the user leaves that task, and later selects the e-mail app from home,
we may like them to return to the conversation they were viewing, not
the picture attachment, since that is confusing. By setting this flag
when launching the image viewer, that viewer and any activities it
starts will be removed the next time the user returns to mail.
-
If an intent is sent, when does it get sent to the onCreate() method
of a new activity instance and when does it get routed through
onNewIntent() of an existing activity?
onCreate is called while creating a new instance of activity. onNewIntent is called if already an activity instance exists and no new instance need to be created, as in case of singleInstance, singleTask and conditionally singleTop (as described above).
Let's suppose the intent gets routed through onNewIntent() of an
existing activity in the task. Which activity does it get sent to? The
one nearest the top or the one nearest the root? Will it always get
sent to an instance of the application's launch activity or can it
sometimes get sent to an activity with the same affinity as the root?
Can it ever get sent to an activity which does not share the same
affinity as the root?
In case of singleTask and singleInstance it has to be root of the task. In case of singleTop it has to be top activity of the task.
Finally, how is this all affected by the various launch modes
(standard, single top, single instance, single task) of the activities
in the task?
I hope the explanation provided till now, answers it.
Update 1:
Here is the Launcher code which adds the flags to intent:
void processShortcut(Intent intent) {
....
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
....
}
void startActivitySafely(Intent intent) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
...
startActivity(intent);
}
Your best bet is to read through the Developer docs here: http://developer.android.com/training/basics/activity-lifecycle/index.html
There is a flow chart in the first lesson(http://developer.android.com/images/training/basics/basic-lifecycle.png) which provides an excellent graphical representation of the Android activity life-cycle.
How do I get my app to appear in on the screen after it has been replaced by some other screen/activity? Some network event occurs and my application wants to reappear in the foreground (presumably in a polite manner).
I think I need to do something like:
contxt.startActivity(myActivity);
I don't want to create another instance of my app or cause it to restart, but I want it to appear.
Use FLAG_ACTIVITY_NEW_TASK
Intent intent = new Intent(contxt, myActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
contxt.startActivity(intent);
In your myActivity onNewIntent is called. I assume myActivity is the top activity in your app current stack
I'm trying to resume an activity from within a broadcast receiver's onReceive() method as follows:
Intent i = new Intent(context, TimerSet.class);
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
However the activity (TimerSet.class) is recreated instead of resumed. The only recommended solution I found to this problem was to use the FLAG_ACTIVITY_REORDER_TO_FRONT but I'm already using it.
Also, using Intent.FLAG_ACTIVITY_NEW_TASK doesn't fit my use case but I get the following exception when I do not provide it:
android:util.AndroidRuntimeException: Calling startActivity() from outside of an
Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you
want?
I am not sure whether this is exactly your problem or not, but I have a situation where I got a notification and I want to start my app without starting a new instance (if it's already running)
I finally figured out that these will work. The FLAG_ACTIVITY_NEW_TASK will not start a new instant if the activity has already been running. However, it will add it to the existing stack. Therefore, we can do a FLAG_ACTIVITY_CLEAR_TOP, so back will bring user to the home screen but not the previous state.
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
remove FLAG_ACTIVITY_NEW_TASK flag.
also add this flag ->FLAG_ACTIVITY_CLEAR_TOP. This would prevent new activity to be created if already present.
I know there's been a few posts for what I'm about to ask but I can't find any with the right answer.
From my understanding, if your main activity's (let's call it A) launchMode is set to singleTask, and A has initiated activity B then a click to the home button will destroy the history stack and re-launching the application will take you back to A and not B.
I have launchMode set to singleTask because I have a persistent notification and I don't want to have multiple instances of the main activity to appear whenever the user clicks on the notification.
Is there something I'm missing that would allow me to cater for both?
So I'm asking if there's a way I can ensure that whenever the user wishes to launch the app, from the notification or not, to take him back to the last (current) activity.
If I change launchMode to singleTop it works but I get multiple instances of the main activity whenever I launch it.
Thanks
Andreas
Have you tried setting launchMode to singleTop to all the activities in your app?? Because what i get from your query is that the main activity isn't singleTop, so that might lead to another instance of the main activity being called once the main activity is launched from the activity that was launched from the notification activity.
Or you can specify the launchMode as an attribute to the application tag itself in the manifest.
I use the following code to avoid multiple instances of the activity
Intent intent=new Intent(this,RICO.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0);
Changing manifest doesn't look appropriate to me
I'm having issues with both the approches.
The notification works flawless only in this condition:
- using the back button in the main activity (with the history containing only the that activity)
- not using the Home button
- not using the notification IF the activity you are calling is on top and active
In any other case, the notification cannot anymore call on the foreground the activity by using "new Intent(...)"
I've found the alchemical combination of manifest options and intent's flags for getting what I needed:
Intent intent= new Intent(this, YaampActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
using these options
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true"
inside the element.
Now I've a notification which spawns the main activity (if that activity is not already in the foreground) and its behavior is correct even if the activity is "closed" by pressing the home button and/or the back one.