As per the documentation of FLAG_ACTIVITY_MULTIPLE_TASK:
This flag is used to create a new task and launch an activity into it.
This flag is always paired with either FLAG_ACTIVITY_NEW_DOCUMENT or FLAG_ACTIVITY_NEW_TASK. In both cases these flags alone would search through existing tasks for ones matching this Intent.This flag is ignored if one of FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_NEW_DOCUMENT is not also set.
When paired with FLAG_ACTIVITY_MULTIPLE_TASK both of these behaviours are modified to skip the search for a matching task and unconditionally start a new task. When used with FLAG_ACTIVITY_NEW_TASK, a new task is always started to host the Activity for the Intent, regardless of whether there is already an existing task running the same thing.
Because the default system does not include graphical task management, you should not use this flag unless you provide some way for a user to return back to the tasks you have launched. See FLAG_ACTIVITY_NEW_DOCUMENT for details of this flag's use for creating new document tasks.
So this means that if I have an activity 1 which opens activity 2 with both of these flags, the 2 activities are opened in different stacks and are thus available as different tasks in recent.
However my question is to understand the behaviour of back button in this scenario.
ideally when activities are launched without any flags, pressing back on them destroys the activity and it is removed from the backstack. if there are no activities, the system exits the app but keeps the backstack with the last activity.
however in this case when i press back, 2 different beaviors are observed:
(Scenario: An app that launches Activity1 which launches Activity2 which launches Activity3 via startActivity(Intent(...).also{it.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK}) )
if i just press back on Activity 3 , i move to Activity 2 . similarly when i press back on Activity 2, i move to Activity 1. and finally when i press back on Activity 1, i exit the app . however, i can see all the activities remain in recents and available to be opened. when i open any of them and press back, then this time, i just get exited from the app into homescreen. video sample
if i press recents on any of the activities while traversing forward, the back-press stops working, and instead i get exited from any of the activity in recents. video sample
So :
why is this behaviour?
Can we assume that if there is just 1 activity in a back-stack, it will show up in recents and not get destroyed, unless the user explicitly clears it from the recents?
Why Activities show a weak link to the caller activity in 1st case and going back to the parent back-stack when press back in 1st scenario?
Related
I have to app stacks like this:
A->B-C->D
X->Y
In Y I want to use getLaunchIntentForPackage("package") to get to D, however I get to
A. What launch mode should I use etc? I would like to use singleTask for ABCD if possible.
Also when in D and I press home and launch via the Icon for A again then depending on launch mode I don't get to D (that I would like), but to A. DCB are cleared.
I tried a lot of things but I just don't understand how it works. I don't seem to get a consequent behaviour.
getLaunchIntentForPackage just gives you the Main activity of package. In your first app i am guessing it is A. So if you try starting it from 2nd app (X->Y), it will start A and not D which is a different activity. So you are not getting to D.
When you press home and click launch icon for first app, if there is already a task for it, it is brought foreground. Looks like A, the app's main activity (A) gets invoked - not the stack top activity D. And B->C-D get cleared because you may be using singleTask launch mode for Activity A, which tries to take you back to activity in existing task.
So what you observe is expected. The launch modes are defined per activity, not per app/package level.
So to get from Y to D, you need to start activity D using intent flag set to FLAG_ACTIVITY_NEW_TASK, this will ensure you get to the existing D, that is already on top in your first task (A->B->C->D)
To do the same from home screen (launcher icon) you may need to try below options for SingleTask mode, since you want to preserve the existing stack (A->B->C-D) , and simply want get to D; Quoting from the link:
"+ For launchMode=singleTask if there is an intent_filter in the
manifest the task stack is always cleared after returning to Home and
re-launching (returns to main activity instead of last activity).
+ For launchMode=standard re-launch from Home instead returns to last
activity in task (as expected).
+ If there is no intent_filter listed then even with
launchMode=singleTask re-launch from Home returns to the last activity
in the task"
So, set your main activity launch mode to standard, it will let you preserve the stack as-is (2nd +point above). You can still start this in singleTask mode using intent-flags (eg: FLAG_ACTIVITY_NEW_TASK) from your other app. Intent flags override manifest launch modes, see here
This is pretty elaborate, so I'll try and be as clear as possible. Currently, my application needs to reload some data when it is launched. I am loading this data in the onCreate/onRestart methods of my main activity. However, I need to be careful to not reload the data if the user never leaves the current task.
For instance, if I need to start the in-built Contacts application from my main activity to select a contact, then my main activity will pause/stop while I am selecting a new contact. When I return to my main activity, onRestart will be called. In this case, because I have never left the Application's task, I do not want to reload the data. This means some check needs to be included here to determine if the user has come straight back from the Contacts app without ever leaving it.
Otherwise, if the user exits the Contacts app while it was open and re-launched the app from the launcher menu (or recent apps list, etc) - I want to close the Contacts app and reload the data when the Main activity's onRestart method is called.
Is there a way to do this without using any user-frightening permissions (e.g. GET_TASKS). I have been stuck on this problem for a long time now so any help would be GREATLY appreciated :)
Tyvm,
B. Campbell
I learned a lot from the Android docs:
Tasks and Back Stack
Navigation with Back and Up
Application Structure
Here is some highlight:
A task is a collection of activities that users interact with when
performing a certain job. The activities are arranged in a stack (the
"back stack"), in the order in which each activity is opened.
When the current activity starts another, the new activity is pushed
on the top of the stack and takes focus. The previous activity remains
in the stack, but is stopped. When an activity stops, the system
retains the current state of its user interface. When the user presses
the Back button, the current activity is popped from the top of the
stack (the activity is destroyed) and the previous activity resumes
(the previous state of its UI is restored). Activities in the stack
are never rearranged, only pushed and popped from the stack—pushed
onto the stack when started by the current activity and popped off
when the user leaves it using the Back button.
It means when your application pick up a contact, the activity stack in the task might be:
| Activity A in Contacts |
| Activity B in Your App |
| Activity C in other app|
When the user leaves a task by pressing the Home button, the current
activity is stopped and its task goes into the background. The system
retains the state of every activity in the task. If the user later
resumes the task by selecting the launcher icon that began the task,
the task comes to the foreground and resumes the activity at the top
of the stack.
Even if the user exits the Contacts app while it was open and re-launched the app from the launcher menu or recent app list, the activity stack is still like the one we see above. The only possibility that may make your acitity start from another task is another app(might be a launcher app) that start the main activity in a new task by startActivity flags, which is a rare case. In this rare case, you can simply check the uid of the app who start your activity by Binder.getCallinguid() to differentiate it.
EDIT based on comments:
You may check if activity was opened from history by FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY:
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0){
}else{
}
You can also look into:
android:excludeFromRecents if you don't want your app start from recent apps
android:finishOnTaskLaunch
android:stateNotNeeded
If the data you are loading related to the application life-cycle not to a specific activity, you can load it in application class, so it will not be reloaded again unless your application task is killed.
I'm encountering the following scenario in my application:
Installing it and immediately opening it (using the open button
after the installation, instead of the icon in the applications
list).
Navigating through a few activities.
Clicking the home button.
Clicking the application icon to load it again.
Instead of returning to the activity I was in, the initial activity
is loaded. The previous activity is still there and can be accessed by clicking the back button, but there isn't really a way to know that and it looks like the app was completely restarted.
This may be an issue in this app, because in some cases it might require a registration after performing a few actions, and losing the focus on the activity could be very annoying
Is there anything that can be done to avoid that?
There's a couple of flags under Intent that might be helpful. You use them when an Activity is launched:
"FLAG_ACTIVITY_TASK_ON_HOME: If set in an Intent passed to Context.startActivity(), this flag will cause a newly launching task to be placed on top of the current home activity task (if there is one).
FLAG_ACTIVITY_REORDER_TO_FRONT: If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought to the front of its task's history stack if it is already running."
http://developer.android.com/reference/android/content/Intent.html
In my situation, there is one case in which I need to make sure the activity only runs one at a time.
I found if I set the LauchMode of the activity, I can reach the single instance aim, but it won't update the view of the activity.
This activity is launched by startActivityForResult, and we send the URI with the intent to the activity.
Let's discuss with this certain case:
gallery - lauch this activity with imageA.
camera - lauch this activity with imageB.
My request is not to destroy the old activity, but the activity that just received the new intent infomation should refresh the view.
I found a new method, onNewIntent.
This method can refresh the intent before resume. I will try it.
You can have an Activity with a manifest attribute of singleInstance.
As soon as the activity is relaunched , onResume gets called. You can update the view with the new image and invalidate the older View.
<activity ..
android:launchMode= "singleInstance" />
In your manifest, use android:launchMode="singleTask" instead of android:launchMode="singleInstance". Using singleInstance only returns to the existing instance if it is at the top of the stack. singleTask, on the other hand, return to the existing instance of the activity, as it is always at the root of the task.
Then, when your instance is launched, override onNewIntent to update your UI according to the new intent.
Refer to the Andorid documentation for details.
Be careful when using android:launchMode="singleInstance" in multiple activities application.
Here is my test on Pixel 4XL (Android 10)
Example, we have 3 activities (without declare taskAffinity)
A (entry activity)
B (start from A)
C (singleInstance, start from B)
If we start A->B->C. Now A,B in a task and C in a different task.
From C, press back -> will see B, press back -> will see A, press back -> app close. Everything working efine.
However, if we start A->B->C then press Home -> app go to to background.
Now there is 2 situations
User open app again from Recent app.
User will see C screen. Press back -> app will close (don't go to B)
User open app again by click on app icon
User will see B screen. Press back -> will see A, press back -> app will close (don't go to C)
The behaviour after open application is different between both case. But in both case, the application is always available in Recent app and you never able to remove application from Recent app (after remove it will appear again). Only able to remove it by Force stop in Setting.
If you run adb shell dumpsys activity activities, you will see that activities still exist in Stack, but you can not go back to it.
Another case
If we start A->B->C->A. Now A,B,A in a task and C in a different task (A is current visible to user).
From A, press back -> will see B (not C), press back-> will see A, press back -> will see C, press back -> app will close. (at least here we can go back to C)
Now if, we start A->B->C->A then press Home. In both case, user open app again by Recent or click on app icon, user will see A screen. Press back -> will see B, press back -> will see A, press back -> app will close (never see C screen)
Another note
The above example, although application run on 2 tasks but in Recent apps, it only show 1 task.
If you set the taskAffinity, in the Recent apps, you will see 2 tasks. In that case, you can choose the task which contain activity that you want to display but when you press back, it just go back to the activity in same task or close app (not able to go back to activity in another task).
Also, if you click on app icon to open app again, we have the same behavior like above demo.
I think singleTask and singleTop is less complex than singleInstance, so if it is enough for solve your problem, consider to use them instead of singleInstance
I have a Notification which starts an Activity B with FLAG_ACTIVITY_NEW_TASK. The documentation says:
[...] 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 [...]
In the case that the user taps the Notification while in Home Screen or a different app, this works fine. But if the user is in an Activity of my app, let's say in Activity "A", and then taps the Notification multiple times, Activity "B" is started multiple times. This leads to Back key not bringing back to Activity "A".
What am I doing wrong here? And where's the difference between being in home screen or a different app and being in Activity A of my app?
Regards
Now I found out that I had to set a different affinity in the manifest to start the Activity in its own task. That solved my problem, too.
You probably want FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP for the notification activity. That will make sure that there is only one instance of Activity B, and it is re-used .
The difference is that when your Activity A is in the foreground you already have task running , otherwise (most probably) not. Read this for more info about tasks.