How to finish destroyed Activity - android

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.

Related

Android : Returning to prior Activity

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.

How to Obtain a Reference to the Most Recently-Visited Activity

I am creating a simple application that entails multiple activities. It is necessary for my application to resume one of two activities based on a certain system preference. Because of this, I have implemented a Dispatcher class that is linked to action.MAIN, that then starts the correct Activity by examining the system preferences within its onResume() callback.
The Problem
Since I am doing this in onResume(), when the back button is pressed on any of the resulting activities, the Dispatcher is resumed, and as a result it again tries to start the same Activity again. This prevents the user from ever leaving the application unless they press the home button.
Some Code
Here is some code excerpts from my classes to help clarify my situation:
Dispatcher.onResume(void) : void
protected void onResume() {
super.onResume();
if(!handled){
Intent activity_switcher;
//If a game is active...
if(manager.isGameActive()){
//Start the the Game Manager with the appropriate game.
activity_switcher = new Intent(this, GameManager.class);
}
//If a game is not active...
else{
//Start the Game Menu
activity_switcher = new Intent(this, Menu.class);
}
//Start the appropriate activity
startActivity(activity_switcher);
overridePendingTransition(0, 0);
}
else{
//Finish the application
finish();
overridePendingTransition(0, 0);
}
}
Some of the variables are as follows:
manager is the variable representing my class that interfaces with the system preferences.
handled is the variable that determines if the event has been handled yet (see "Attempted Solution #1" below)
Attempted Solution #1
I have considered implementing a boolean class variable, handled, that is tested before the activities are started. If the value is found to be true, then the Dispatcher simple makes a call to finish(), however if the value is false, then the appropriate activity is started and handled is set to true so that when the application returns to this Activity, it will simply finish.
This will not work, because...
If the user pressed the home button on one of the resulting activities, then when they resume the application, they will be placed on the dispatcher activity, and the application will immediately finish because it thinks that it was handled (the variable is still true).
Attempted Solution #2
I tried to find some way to only run to the Dispatcher once per application life cycle. This makes sense because the user would never want to return to the Dispatcher as it is an Activity that should only be ran once at launch time. So, I included a counter that would be incremented each time onResume() was called, and reduced to 0 each time finish() was called.
This will not work, because...
Again, if the user presses the home button, then when the application is re-launched, the Dispatcher will think that it has already been run once (because there was no call to finish()), and will immediately finish again.
Attempted Solution #3
I figured that if I could somehow determine which activity was previously active, then I could dynamically react to the case where the user pressed the back button, and then and only then inform the Dispatcher that it should finish, then there would be no immediate finishes in the case when the user presses the home button.
This was accomplished by using a static function returningFromMenu() that, when called from the menu Activity, raised a flag in the Dispatcher class that informed the it to finish.
Attempted Solution #4 (Current Implementation)
Simply moving all of this activity switching code into onCreate(). This would enforce the "one time" nature of the Dispatcher.
This might work, because...
This will handle the case where the user returns to the activity (using onResume()), and, since there will be no flags, there is no issue when returning to the application after pressing the home button.
My Question
Is there any way to determine which Activity was last finished, other than having to use a static method or member? I know that this is a very bad practice and would like to avoid it if possible.
Updates
Last Updated: 12 Feb 2013 3:45PM EST
Approach #4 seems to be working for my purposes, however I still think that the question here could be of great use to developers, and will refrain from answering my question until I have given enough time for a more holistic solution to be posted.
The Solution
As is stated in "Updates" above, this problem was solved by simply moving the code into onCreate() so that the switch was made only once. Equivalently, I could have simply made a call to finish() immediately after starting the activity in order to remove the calling activity from the back stack.
Thank you all for your efforts in the comment section!

Pausing/Resuming Activity outside of an activity

I have 2 activities. Main Activity A & Activity B
I do not want Activity A to destroy. I am starting Activity B in a new task.
public static void startActivity(Class<?> startClass) {
Intent intent = new Intent(Constants.getActivity(), startClass);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Constants.getActivity().startActivity(intent);
}`
Constants.getActivity() returns the Context on current activity
startClass is the either activity "A" or activity "B"
Thing is they create/destroy the activities and they leak. Am I doing it wrong? How can I start activity "B" from activity "A" and vice versa keep them both in background when I dont need them.
First of all, what are you trying to do? You should always separate things you want to do in the background from your UI. Think of your Activities are simply a container to show the UI, everything else can be stored and restored from persistent storage or savedinstance bundles.
It is very crucial that you understand the difference between Activity lifecycle and Service lifecycle here.
I'm going to refer to my answer from another question here:
Your app will be in the background, onResume and onPause will both be called, provided that the OS have enough memory to keep the new app and all the old apps.
If you have a long running process that you need while the user not looking at it, use a service.
If you need the user to return the app in the same state, you need to do the work in onResume and onPause to save the state information and initialize your UI again when the user comes back. If you are really worried that it can get killed (well, it shouldn't lose the bundle I think), you can store them in SharePreferences for your app.
If you want to know when the app returns from that specific share intent, use startActivityForResult
You cannot keep an activity "alive" as you said. When an activity is paused, Android system is always able to claim its memory and unload it, at any time.
But if you want to switch from A to B, then maybe A and B can be different views inside a single activity. Maybe you'll want to have a look at http://developer.android.com/reference/android/widget/ViewFlipper.html
When you use tasks, cleanup is very important. You need to cleanup all tasks in the activity. readthis
If the activity does not have a lot of crazy initialization, just use finish and onCreates. Else be aware that onResume will be called often as you switch between activity's. Cleanup will be crucial here. If you dont cleanup, its possible one of your activities (with dead object reference from the other cleaned up activity) may come back up from the activity stack and throw exceptions. Its very difficult to debug this kinda exception.

Want to start a new activity but let the original activity finish first

I have an Android activity we'll call A which has a button and another activity B. When the user clicks the button in Activity A, I'd like to finish A (let both onStop and onDestroy finish running) and then start up the instance of B. When I put a finish() and startActivity() call in the button click listener, the instance of B starts up before the old instance of A finishes. Can someone help me figure out a way to do what I'm looking for?
What you are looking for is not possible and actually is against Android's activity lifecycle implementation.
Correction
It is possible with android:noHistory="true" tag in your manifest, but for what you are trying to do it seems wrong (read the EDIT)... Messing with the activity stack makes a non intuitive application!
Android OS doesn't let you control when activities will be removed from memory (or killed), and therefore all these fancy "Task killers" are so popular (DONT use them, they only make things worse).
When your activity's onStop() is being called, the activity stops completely, and it just hangs in your memory, but that's fine...
If you want to reset the state of activity A, or close the app when exiting activity B, just create a set of rules in both onResume() and onStop(), you can do everything you wish by creating a set of rules in those functions.
for example: have a boolean in activity A that turns true just before calling activity B,call finish() on your activity A's if this boolean is true
I suggest that you take a look at Android's Activity lifecycle diagram, and make sure that everything you do follows the best practice.
EDIT
I saw your comment, it seems like you are trying to create things that are already in your memory, don't recreate them, it's a waste of CPU time, memory, and battery.
Instead, create a static class with a singleton that will hold all your shared data !
I believe you're looking for
onPause()
which is what gets called when the activity is sent to the background. You can do whatever cleanup you want in there. onStop should only be called when a user is exiting out of your program (or launching another one)
onPause is a better place to do this cleanup. See the Saving Persistent State section of the Activity doc.
When an activity's onPause() method is called, it should commit to the backing content provider or file any changes the user has made. This ensures that those changes will be seen by any other activity that is about to run. You will probably want to commit your data even more aggressively at key times during your activity's lifecycle: for example before starting a new activity, before finishing your own activity, when the user switches between input fields, etc.
While I'm not definite that your cleanup is for user changes, the bold sentence above implies that onPause will complete before the next Activity is created. Of course that probably implies that you'll have to move some setup to onResume...
Alternatively, you could move all your cleanup code to a method, let's just call it cleanup and then just call it before you start activity B. You'll have to put in appropriate guards for your onDestroy cleanup too of course.
override finish() method.
implement cleanUp() method.
create boolean isClean=false in the activity
in cleanUp() write your clean up code.
call cleanUp() in your finish()
check for isCleaned in finish() or in cleanUp() if its true then ignore the clean
now before you start B , call cleanUp() and set isCleand=true
after you call B , call finish()
Start activity A
from inside A startService(c) and finsh A
from inside the service , start Activity B

WeakReference to Activity (Android)

In my Android app, when a user tries to transition from one activity to another, there may be some global state that indicates they need to complete some other action first.
To accomplish this, I've written a class with the following code:
private static WeakReference<Activity> oldActivityReference;
private static Intent waitingIntent;
public static void pushActivity(Activity currentActivity, Intent newActivityIntent) {
Intent blockingIntent = ThisClass.getBlockingActivity();
if (blockingIntent != null) {
ThisClass.oldActivityReference = new WeakReference<Activity>(currentActivity);
ThisClass.waitingIntent = newActivityIntent;
currentActivity.startActivity(blockingIntent);
return;
}
currentActivity.startActivity(newActivityIntent);
}
When the blocking activity finishes, it calls ThisClass.blockingActivityFinished(). That will check to see if the weak reference to the old activity still exists and, if so, launch the original intent from that activity. If not, it will launch the original intent from my application's context.
My question is,
Does this sound sane? Are there any potential memory leak issues with this technique? Is there a better way to accomplish this?
EDIT -
To be clear, the types of events that might trigger an interruption are 1) a server ping indicating that the current app version is deprecated 2) any server RPC indicating that the user's credentials are no longer valid. I do not want to add logic to every Activity to handle checking for these, and resuming business as usual once they complete. That is a violation of DRY, and error-prone in a team environment.
Does this sound sane?
I'd never use this technique. Mutable static data members are dangerous, WeakReference notwithstanding. In particular, I'd expect this to fail if the user does the unthinkable and, say, uses their phone as a phone, or otherwise leaves your application flow for an extended period of time. Your activities may be destroyed and your process terminated to free up RAM, yet the activities would remain in the task and might be reactivated. At that point, your state is whack, because the statics got nuked.
Are there any potential memory leak issues with this technique?
You're leaking an Intent.
Is there a better way to accomplish this?
For the purposes of the rest of this answer, I'm going to refer to your starting point as Activity A, the "some other action" as Activity B, and the desired end as Activity C. So, in your code, newActivityIntent is for Activity C, blockingIntent is for Activity B, and currentActivity is Activity A.
Option #1: Put the decision-making process in Activity C, rather than Activity A. Have Activity C check the condition in onCreate() and immediately calls startActivity() for Activity B if the conditions require Activity B to be shown.
Option #2: Leave the decision-making process in Activity A, but pass the boolean (e.g., true for "we gotta show Activity B") in an Intent extra for the startActivity() call for Activity C. Activity C checks the boolean in onCreate() and immediately calls startActivity() for Activity B if the boolean says so.
In these options, you avoid the statics.

Categories

Resources