after looking up the 'Defining launch modes' section of the 'Tasks and Back Stack' guide , I dont understand exactly the behavior of an activity declared with "singleTask".
assume that I start a singleTask-activity (via startActivity method ) when there is an instance of that activity laying at the bottom(root activity) of a background task,
as the article describes, the Intent I use to start singleTask-activity will be delivered to the instance, in this case, does the instance bring itself to top of its task and its task to foreground, or just bring its task to foreground without bringing itself to top of its task?
Do I make myself clear? pls help! thanks in advance
I was/am having the same question actually.
After reading replies here on stackoverflow and doing some experiments, I believe that when a singleTask activity is launched while it's already in a background task, the system will kill all other activities at the top of the stack and resume the singleTask activity.
For sure, this is not what I got out of the documentation though.
Here is my theory:
Launch Modes: Activity A: singleTask , Activity B: standard
experiment 1
Launch A ; stack: [A]
Launch B from A; stack: [A|B]
Press home; stack: [A|B]
Launch A from launcher; Stack: [A] (onDestroy was called on B
before onResume() on A)
experiment 2
Launch A ; stack: [A]
Launch B from A; stack: [A|B]
Launch A from B; stack: [A] (onDestroy was called on B
after onResume() on A)
In my case, I had to use android:alwaysRetainTaskState="true" although this is not ideal as I do want the stack to be cleared after, say, 10 minutes as this recommendation makes sense:
If the user leaves a task for a long
time, the system clears the task of
all activities except the root
activity. When the user returns to the
task again, only the root activity is
restored. The system behaves this way,
because, after an extended amount of
time, users likely have abandoned what
they were doing before and are
returning to the task to begin
something new.
from the Tasks and Back Stack guide.
I hope this helps other people.
If the Activity already exists it will call onNewIntent() for that Activity. Since an Activity is always paused before receiving a new Intent, onResume() will be called shortly after onNewIntent() is received. The fact onResume() is called means the Activity will come to the foreground and be visible to the user.
Then how does FLAG_ACTIVITY_CLEAR_TOP is different from Single Task?
Is the difference only that FLAG_ACTIVITY_CLEAR_TOP is a subset of single task, which only works the same way as but only if the activity being invoked is part of the current task?
Related
When you're in an an Activity (we'll call it A), and you invoke a subsequent Activity (B), perhaps as a result of clicking a button in A, and then RETURN to that prior Activity A, either by clicking the Back button or explicitly calling finish() from within B, it causes A to be completely rebuilt, calling its constructor and its OnCreate() method, etc.
Is there any way to prevent that from happening, so that it actually does return to the prior, already existing, Activity A?
Correct me if I'm wrong, but it should not call onCreate() here's a gross over simplification, but let's say activity's are managed much like a simple stack, let's call it AppStack
When a onCreate() for Activity A is called, the OS pushes the Activity Instance onto the AppStack
________ _________________
Activity|
___A____|_________________
When you click a button on Activity A, it launches a new intent to Activity B
Intent actB = new Intent(this, ActivityB.class);
and subsequently puts Activity A into Stopped state
When Activity B's onCreate() is called the OS pushes that Activity Instance onto the AppStack
________ __________________
Activity|Activity|
___A____|___B____|_________
Now if you call finish() or super.onBackPressed() in Activity B, the OS will pop() the Activity from the AppStack
________ __________________
Activity|
___A____|__________________
When the OS returns to the previous activity, it sees that it is Stopped and begins the process of Resuming it through onResume().
Now if there is some data that you require to be persistent, you can add it in by Overriding onResume()
Check out the activity lifecycle docs, for more info:
This is by design:
If an activity is paused or stopped, the system can drop it from memory either by asking it to finish (calling its finish() method), or simply killing its process. When the activity is opened again (after being finished or killed), it must be created all over.
See Activity Lifecycle. It's also why the Service class exists:
A Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application.
It's not a typical scenario but when onCreate() is called when going back to that activity that means the Android OS kills it in the background.
Reason: Android is experiencing some memory shortage so killing some of the background task will be a must.
Is there any way to prevent that from happening?
No, you don't have a control over it, there are many reasons why its having a memory shortage e.g. other app installed that certain device is consuming more than expected. Although you can handle this use-case by storing the current information in onSaveInstanceState() and recovering the value from onCreate().
Calling finish() on ActivityB or pressing back will just destroy ActivityB.
ActivityA will not be completely rebuilt. This means it will not call onCreate method. It will just call onResume.
This is the normal behaviour.
However, on special situations, the system could destroy ActivityA (maybe because it needs memory to perform another task), so when you go back to it, the system will have to rebuild it.
To simulate this situation, there is a setting that you can check/uncheck, called "Don't keep activities".
If you have it checked, you will be simulating the situation explained above, it will always destroy the ActivityA as soon as it is not shown, and when you come back to it, the system will have to rebuild it calling onCreate.
My app has a launcher activity that looks at the intent used to call it and calls one of many activities based on the intent (let's call the child activity B). The launcher activity also launches an AsyncTask to do some clean up in SharedPreferences in the background. Once that background task finishes, the launcher activity calls finish() to terminate itself.
If the background task is still not finished when activity B terminates, I want to avoid returning to the launcher activity. How can I do that?
I am thinking of using android:noHistory="true" in the definition of the activity in the manifest. However, the description of noHistory says:
"Whether or not the activity should be removed from the activity stack and finished (its finish() method called) when the user navigates away from it and it's no longer visible on screen". So I am afraid that the activity might be terminated before it finishes running its background task.
Is my background task guaranteed to be allowed to run to completion? Is there some better way to do this?
Make is simple. Start a background task in your application instance and clean-up preferences there. This will allow you to finish your "dispatcher" activity immediately after it started a next required activity.
Regarding excluding the activity from the back stack. There are two options. You can either define "dispatcher" activity with android:noHistory="true" in manifest file, or start "dispatcher" activity with FLAG_ACTIVITY_NO_HISTORY. Both options will exclude the activity from back stack.
Also, if "dispatcher" activity is in background, Android might decide to destroy it at any time. This might lead to unexpected results. With proposed soliton you have no such issue either.
I'm talking about programming in android.
In early days I thought that, finish() closes current activity and go back to the previous in Activity stack, and System.exit(0) closes the whole application.
But I was wrong.
I made a small experiment and understood that Both will finish only the current Activity.
The only differences that I could notice is that, in Android 2.3.3
The ActivityResult is propagated back to onActivityResult() using finish(). Whereas onActivityResult() not called for System.exit(0).
But in Android 4.2.2, onActivityResult() is called for both! and Intent was null for exit().
(I tested only in these 2 devices)
There is a time lag when using exit() whereas finish() is faster.(seems like more background operations are there in exit())
So,
what's the difference between two?
In which situations, I can use exit()?
I believe there is something more that I'm missing in between the two methods.
Hope somebody can Explain more and correct me.
Thanks
EDIT UPON REQUEST:
Make an Android application with 2 Activities. Call second Activity from Launcher activity using Intent. Now, inside the second activity, upon a button click, call System.exit(0);.
"The VM stops further execution and program will exit."????(according to documentation)
I see first activity there. Why?
(You are welcome to prove that I'm wrong/ I was right)
Actually there is no difference if you have only one activity. However, if you have several activities on the stack, then:
finish() - finishes the activity where it is called from and you see the previous activity.
System.exit(0) - restarts the app with one fewer activity on the stack. So, if you called ActivityB from ActivityA, and System.exit(0) is called in ActivityB, then the application will be killed and started immediately with only one activity ActivityA
According to android Developer -
finish()
Call this when your activity is done and should be closed. The
ActivityResult is propagated back to whoever launched you via
onActivityResult().
System.exit(0)
The VM stops further execution and program will exit.
According to the documentation, The program will exit.
But it seems a bug in the documentation. In case of a java program, it is correct. But coming to Android, You will see the previous Activity from the stack.
Since Android coding is done using java coding, most of the documentation is same as those for java.
From documentation,
System.exit(0)
The VM stops further execution and program will exit.
For Android aspect, we have to replace the word 'program' with something else. May be Activity or Context.
Sa Qada answer is correct after my testing.
finish will close this activity and back to prevous.
but exit will close current activity too and empty all the activity in freeze and start again the previous activity
Actually there is no difference if you have only one activity.
However, if you have several activities on the stack, then:
finish() - finishes the activity where it is called from and you see
the previous activity. System.exit(0) - restarts the app with one
fewer activity on the stack. So, if you called ActivityB from
ActivityA, and System.exit(0) is called in ActivityB, then the
application will be killed and started immediately with only one
activity ActivityA
As I understand it, an activity being destroyed is not equivalently to an activity being finished.
Finished
The activity is removed from the back stack.
It can be triggered by the program (e.g. by calling finish()), or by the user pressing the back key (which implicitly calls finish()).
Finishing an activity will destroy it.
Destroyed
The Android OS may destroy an invisible activity to recover memory. The activity will be recreated when the user navigates back to it.
The activity is destroyed and recreated when the user rotates the screen.
Reference: Recreating an Activity
So how do I finish a destroyed activity? The finish() method requires an Activity object, but if the activity is destroyed, I have no Activity object - I am not supposed to be holding a reference to a destroyed activity, am I?
Case study:
I have an activity a, which starts b, which in turn starts c (using Activity.startActivity()), so now the back stack is:
a → b → c
In c, the user fills out a form and tap the Submit button. A network request is made to a remote server using AsyncTask. After the task is completed, I show a toast and finish the activity by calling c.finish(). Perfect.
Now consider this scenario:
While the async task is in progress, the user switches to another app. Then, the Android OS decided to destroy all 3 activities (a, b, c) due to memory constraints. Later, the async task is completed. Now how do I finish c?
What I have tried:
Call c.finish():
Can't, because c is destroyed.
Call b.finishActivity():
Can't, because b is destroyed.
Use Context.startActivity() with FLAG_ACTIVITY_CLEAR_TOP so as to raise b to the top, thus finishing c:
// appContext is an application context, not an activity context (which I don't have)
Intent intent = new Intent(appContext, B.class); // B is b's class.
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
appContext.startActivity(intent);
Failed, appContext.startActivity() throws an exception:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
Edit: Clarification: I need to wait until the async task finishes and decide whether to finish c based on server's response.
android.util.AndroidRuntimeException: Calling startActivity() from
outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK
flag. Is this really what you want?
This exception used to occur when you are starting an activity from
the background thread or service. You need to pass
FLAG_ACTIVITY_NEW_TASK flag whenever you need the "launcher"
type of behavior.
Just add mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); to avoid this exception.
The way you are trying to kill the activity is not recommended, let the
android handle itself. There isn't any meaning to finish an activity
which is already destroyed.
Now, what you can do?
If you are facing problem in finishing activity when app is not in foreground, what you can do is to implement a security check which will finish the activity only when app is in foreground to go to back-stack activity or else just skip that step.
I think you are trying to kill the activity when app is in background. It seems a little bit difficult to do so, but you can make use of onUserLeaveHint to decide when app is going in the background in-order to finish the activity or you can finish the activity by adding finish(); in onStop(). Just make sure that asynctask's onPost() don't finish it again in-order to avoid the exception.
Have a look at android:clearTaskOnLaunch attribute and set it to true.
Google Doc says about this attribute is:
for example, that someone launches activity P from the home screen,
and from there goes to activity Q. The user next presses Home, and
then returns to activity P. Normally, the user would see activity Q,
since that is what they were last doing in P's task. However, if P set
this flag to "true", all of the activities on top of it (Q in this
case) were removed when the user pressed Home and the task went to the
background. So the user sees only P when returning to the task.
and i think this is the exact case which you want.
Hope this will give you some hint to achieve your desired task.
you can broadcast your action from the onPostExecute method in c and register a broadcast receiver to receive for that action in a and b. Then do finish in that receiver onRevice method
In c , AsyncTask,
void onPostExecute(Long result) {
----
Intent intent1 = new Intent("you custom action");
context.sendBroadcast(intent1);
}
In a and b
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
finish();
}
},new IntentFilter("you custom action"));
Personally, I'd use the notification bar to notify the user of the status of his query.
This way, I'd avoid the entire issue of having an unfinished activity. And I'd only keep the activity unfinished only if the user had not clicked on the submit button yet.
Regarding android manual onDestroy() called exactly before activity is destroyed, so you can call finish in it (even you can stop your bg thread before killing the activity completly).
We can assume that if activity was killed we don't interested in bg thread either, and for example if bg thread is to download image or etc that needs to be completed - so you have to use service instead of asynctask.
Can't finish a destroyed activity directly, so just finish() it in its onCreate() (suggested by #Labeeb P). Here's how:
If the activity is already destroyed when trying to finish it, save a boolean flag somewhere instead.
if(activity != null)
{
// Activity object still valid, so finish() now.
activity.finish();
}
else
{
// Activity is destroyed, so save a flag.
is_activity_pending_finish = true;
}
If the flag needs to stay even if the app is destroyed, use persistent storage, e.g. SharedPreferences (suggested by #Labeeb P).
In the activity's onCreate(), check the flag and call finish().
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if(is_activity_pending_finish)
{
is_activity_pending_finish = false; // Clear the flag.
// This activity should have been finished, so finish it now.
finish();
return;
}
...
}
If there're multiple instances of the same Activity class, you may need something more than a boolean flag to identify the specific instance of activity to finish.
Calling finish() in onCreate() is actually a legimate operation, as it is mentioned in the doc:
... you might call finish() from within onCreate() to destroy the activity. In this case, the system immediately calls onDestroy() without calling any of the other lifecycle methods.
Other considerations:
It may not be a good idea to finish an activity while the app is in background, especially if it is the only activity. Make sure that you don't confuse the user.
For better user experience, if you finish an activity while the app is in background, you may want to inform the user. Consider using toasts (good for short notices) or notifications (good for long operations that the user may have forgotten)(suggested by #Stephan Branczyk and #dilix).
Of course, an activity being destroyed doesn't necessary mean that the app is in background (there might be another foreground activity). Still, the above solution (calling finish() in onCreate()) works.
When the system tries to destroy your Activity, it calls onSaveInstanceState. In here you can call finish(). That's it.
Warning: I've never tried this, so I don't know for sure if there are any issues with calling finish() from onSaveInstanceState. If you try this, please comment and let me know how it works out.
Sorry for answering this almost 10 years later.
In my understanding the premise of the question is wrong, mainly this part:
"While the async task is in progress, the user switches to another app. Then, the Android OS decided to destroy all 3 activities (a, b, c) due to memory constraints. Later, the async task is completed. Now how do I finish c?"
In my understanding if the operating system decides to destroy all three activities due to memory constraints, it won't destroy only them, but the whole process, and this should be including the AsyncTask in question. So, the async task won't be able to complete as well.
Resource: https://medium.com/androiddevelopers/who-lives-and-who-dies-process-priorities-on-android-cb151f39044f
mainly this line from the article: "Note that while we’ll talk about specific components (services, activities), Android only kills processes, not components."
In todays world, I guess that a WorkManager would be used for running work that needs to be guaranteed to run.
What is the real use of onNewIntent() in the activity life cycle and how do we use this method?
This is called for activities that set launchMode to "singleTop" in their package, or if a client used the FLAG_ACTIVITY_SINGLE_TOP flag when calling startActivity(Intent).
If you set to single top, the activity will not be launched if it is already running at the top of the history stack. It will not relaunch just show from stack.
Check this link onNewIntent()
In manifest.xml, in an activity tag set launchmode="singleTask"
Above answers are incomplete.
In case the activity 'a1' of Application 'A1' has launch mode "singleTask" or "singleTop" and is already alive (in task t1) but paused, and now another task (say Task t2) (usually another android app) sends an intent to activity a1 of application A1, then instead of creating another instance of activity in task t2, android resumes a1 from task t1, by issuing a callback to onNewIntent(intent) method in a1.
Tasks and back stack is an important concept, no blog explains better than android documentation itself.