Lifecycle of Activity instances - android

I have the following Activity:
public class StartActivity extends Activity
{
String str = "somestring";
int number = "1";
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Code here
}
}
I read the following on the Android docs (http://developer.android.com/reference/android/app/Activity.html)
If an activity is completely obscured by another activity, it is stopped. It still retains all state and member information, however, it is no longer visible to the user so its window is hidden and it will often be killed by the system when memory is needed elsewhere.
Does this mean that my class instances (str and number) are "alive" and available unless onDestroy is called or memory is needed after onPause or onStop is called?

If in your caller activity, you have called finish() method, then no instances of previous activity will be alive since you have finished caller activity. All the instances will be garbage collected.
But if your caller activity, does not call finish(), then the activity will no longer be visible but, it will be present on the activity stack maintained by the operating system. The caller activity (i.e. the previous activity) may get finished in cases when the device is low in memory like in cases, ex.: if the activity calls Camera (which requires rich resources), in that case the previous activity may get destroyed.
So in your case, the variables will be alive even if StartActivity is not the present visible activity.

Related

Concurrent Instances of Activity in one application?

I have an Android application with a single Activity (MainActivity). I also have a static state variable (foo) that needs to be started and stopped with MainActivity. The lifetime of foo must match the entire lifetime of MainActivity, not its visible lifetime, nor its foreground lifetime. Here's the basic gist:
static Foo foo;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
foo.start();
}
protected void onPause()
{
super.onPause();
if (isFinishing())
disposeFoo();
}
protected void onDestroy()
{
super.onDestroy();
disposeFoo();
}
private void disposeFoo()
{
if (foo.isRunning())
foo.stop();
}
Every now and again I will get a crash report that says: Foo has been started while already running.
I can start and stop MainActivity all day long from the app launcher and this crash won't occur. As far as I know, no one is calling startActivity on MainActivity either.
Is it expected behavior that a new instance of MainActivity will be created, and onCreate called on it before the onDestroy is run on the old instance all within the same application? In what circumstances would this occur? Is there a different pattern I should be using to initialize libraries, databases, and other singleton objects?
Your app is in very often killed if some other application with higher priority (generally, if it's in the foreground it's higher priority) needs the resources. This is due to the nature of mobile devices having relatively limited resources. You will find that it's static variables may be null once you return to it, so static variables for a longer periods of time in Android is a bad idea.
You should save your data somewhere more durable. You might find this article on general Data Storage to be useful. This question should be relevant too: Saving Android Activity state using Save Instance State
Because onDestroy is not called if, for example, activity was recreated because of screen rotation.
From the Activity documentation:
The final call you receive before your activity is destroyed. This can happen either because the activity is finishing (someone called finish() on it, or because the system is temporarily destroying this instance of the activity to save space. You can distinguish between these two scenarios with the isFinishing() method.
So you should call foo.start/foo.stop in onStart/onStop or onResume/onPause.
--
Update:
If I understand correctly, the problem is that you are tied to a singleton/monostate object Foo the should be unique for all objects, and must be destroyed when ALL activities are destroyed.
The problem is that nothing can guarantee that only one instance of activity has a runinng Foo, because onDestroy can be called after a new instance is created.
So the solution is to use an Instance counter:
public class Foo {
private int instCounter = 0;
public synchronized void start() {
...
++instCounter;
}
public synchronized void dispose() {
--instCounter;
if (instCounter == 0) {
// dispose
}
}
This should do the trick.

How do tell if the Activity state has been destroyed and needs to be recreated

I have a list activity which does the usual list activity type stuff. All of the activity setup was being done in the onCreate() method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_streams);
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this));
mActionBar = getActionBar();
mStreamListAdapter = new StreamListRowAdapter(this, R.layout.stream_list_row);
mStreamListView = (ListView)findViewById(android.R.id.list);
mStreamListView.setAdapter(mStreamListAdapter);
}
I do not have onStart() and onResume() overrides. I can successfully navigate out of this activity based on the menu buttons pressed and then navigate back to this activity. When I navigate away from this list activity via startActivity(newIntent), I do not call finish, but I do call finish in the newIntent activity, when coming back to this list activity. Everything works fine, unless either the ListActivity itself or one of the new Activities have been sitting in the background for a long time (i.e. I switched to another app). Long time is measured in hours. Sometimes (NOT always), when I come back to my app after this delay, the list activity does not display the list, the menu is not displayed either, just a blank screen with the activity title displayed in the ActionBar.
I realize that I need to handle coming back to the list Activity via onResume() (and maybe onStart()). But onResume() is always called after I navigate back to this list activity, how do I really know if the variables representing the ListAdapter and ListView have actually been destroyed by the OS and need to be recreated inside onResume(), I don't want to recreate them every time onResume() is called. Can I just check to see if they are equal to null? It's hard to test this, as this does not happen very regularly.
Thank You,
Gary
It sounds like you are missing some basic information about Android and Activity's lifecycle. First I want to mention how Android saves it's state. There are 2 important points here. When leaving an Activity it is at risk of being destroyed it calls the hook method
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("myKey1", 1);
outState.putString("myStringKey", "hello");
}
When this is called, this is a chance for you to save all the data that you'd like to persist. This could be ids, strings, arraylists, and nearly any other type of data.
Now that you've saved your data, in the onCreate method of your Activity you are passed back all of these values.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String myStr = savedInstanceState.getString("myStringKey");
int myInt = savedInstanceState.getInt("myKey1", -1);
}
// do the rest of your setup
}
On the first time onCreate is called savedInstanceState is null. To answer your question: "how do I really know if the variables representing the ListAdapter and ListView have actually been destroyed by the OS" this is how you can know. If savedInstanceState is not null, your stuff has probably been destroyed. However, I will say that anytime onCreate gets called that Activity was already completely destroyed anyway.
The other thing I will mention is Android is allowed to kill your backgrounded Activity whenever it needs to reclaim some memory. When it does this, it will call the onSaveInstanceState method for you. Android does not have to restart the application at the "first" or "MAIN" screen of your application. It can start off several Activities in if that's where the user left off. I mention this because it sounds like you're having issues whenever Android decides to create your activities after it clear's it's memory. The best way to make sure your application works well when this happens is to just rotate the phone. Rotate the phone from horizontal to landscape and/or back will cause the Activity to be destroyed and recreated with onSaveInstanceState called and passed back in to the onCreate. Based upon the information you've provided this is the best I can suggest for now.
Cheers.
I have finally figured out what was going on. I have 2 activities, ActivityA and ActivityB, and an Application object. Upon the application launch, ActivityA initializes some globals that are stored in the Application object and then starts ActivityB. When my app is restarted after being in the background for a long time, the onCreate() of the Application is called and then onCreate() of ActivityB is called. ActivityB relies on the variables in the Application object that normally are initialized by the ActivityA, but ActivityA is never called this time around and the globals are not initialized. I am still not clear why the ActivityA just hangs, instead of crashing with some message stating that the variables are null, which is what onCreate() in the Application object sets them to, but regardless, when this occurs, I need to go back to ActivityA() and reinitialize everything as I normally would, when the application is launched.
I now have a boolean in the Application object that indicates whether or not ActivityA has been executed after the onCreate() of the Application object has been called. ActivityB checks this boolean flag, and if not set, then simply start ActivityA to perform the on launch type initialization. I actually have to do this check in pretty much every activity.

How to finish destroyed Activity

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.

onPause() and onStop() in Activity

I am new to Android development and I am still not able to understand the onPause() and onStop() methods in an activity.
In my app, I have a static class that I name Counter, and it keeps the state of variables in memory for the app. My app runs fine in the emulator. What I was trying to test was differential behavior of onPause() versus onStop().
For onPause, I wanted the values stored in the Counter class's members retained, whereas calling onStop() I wanted the counter values reset to zero. So I override onStop() and set the variables inside the counter class to zero. However, in the emulator, I cannot seem to get the app in the Paused state. In the emulator, I open my app, exercise it. Then I hit the home button (not the back button) of the emulator, and launch another app, believing that this would mimic onPause() activity. However, the emulator does not appear to honor this (I am using an armeabi v7a emulator), it seems to always be calling onStop() because my counter values all go back to zero, per my override in onStop(). Is this inherent to the emulator or am I doing something wrong to get my activity into the paused state?
I'm not sure which emulator you are testing with, but onPause is the one method that is always guaranteed to be called when your Activity loses focus (and I say always because on some devices, specifically those running Android 3.2+, onStop is not always guaranteed to be called before the Activity is destroyed).
A nice way to understand the Activity lifecycle for beginners is to litter your overriden methods with Logs. For example:
public class SampleActivity extends Activity {
/**
* A string constant to use in calls to the "log" methods. Its
* value is often given by the name of the class, as this will
* allow you to easily determine where log methods are coming
* from when you analyze your logcat output.
*/
private static final String TAG = "SampleActivity";
/**
* Toggle this boolean constant's value to turn on/off logging
* within the class.
*/
private static final boolean VERBOSE = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (VERBOSE) Log.v(TAG, "+++ ON CREATE +++");
}
#Override
public void onStart() {
super.onStart();
if (VERBOSE) Log.v(TAG, "++ ON START ++");
}
#Override
public void onResume() {
super.onResume();
if (VERBOSE) Log.v(TAG, "+ ON RESUME +");
}
#Override
public void onPause() {
super.onPause();
if (VERBOSE) Log.v(TAG, "- ON PAUSE -");
}
#Override
public void onStop() {
super.onStop();
if (VERBOSE) Log.v(TAG, "-- ON STOP --");
}
#Override
public void onDestroy() {
super.onDestroy();
if (VERBOSE) Log.v(TAG, "- ON DESTROY -");
}
}
I know your question was 6 months ago but in case someone else stumbles on this question:
am I doing something wrong to get my activity into the paused state.
Yes, you are. This:
I hit the home button (not the back button) of the emulator, and
launch another app, believing that this would mimic onPause()
activity.
Hitting the home button will indeed call the onPause() method but because the home button makes your activity no longer visible it will then call the onStop() method (like patriot & milter mentioned).
As per the Activities developer reference (http://developer.android.com/guide/components/activities.html) you can display a dialog or simply put the device to sleep.
Alternatively, you call an activity that will only partially obstruct the calling activity.
So call an activity that creates a window with a view of size:
android:layout_width="100dp"
android:layout_height="100dp"
Which doesn't cover the entire screen, thus leaving the calling activity behind partially visible, thus calling only calling activity's onPause() method.
Clone that activity so that both view sizes are "match_parent" instead of "100dp" and call it and both the onPause() and onStop() methods of the calling activity will be called because the calling activity won't be visible.
There can be exceptions of course, like if the called activity causes an app crash in either of its onCreate(), onStart() or onResume() then the onStop() of the calling activity will not be called, obviously, I'm just talking about the general case here.
The differences between when onPause() and onStop() are called can be pretty subtle. However, as explained here, onPause() will usually get executed when another activity takes focus (maybe as a pop up, or transparent window) while the current activity is still running. If you navigate away from the app completely (for example, by hitting the home button), the activity is no longer visible and the system may execute onStop(). I only say may because, as Alex mentioned, there are some cases where onStop doesn't get called before the Activity is destroyed.
onPause():
"If an activity has lost focus but is still visible (that is, a new non-full-sized or transparent activity has focus on top of your activity), it is paused. A paused activity is completely alive (it maintains all state and member information and remains attached to the window manager), but can be killed by the system in extreme low memory situations."
onStop():
"If an activity is completely obscured by another activity, it is stopped. It still retains all state and member information, however, it is no longer visible to the user so its window is hidden and it will often be killed by the system when memory is needed elsewhere."
Taken from android reference activity class: http://developer.android.com/reference/android/app/Activity.html
If you are emulating Android 4.x you can control how the system handles background activities using Settings -> Developer Options -> Don't keep activities and Background process limit. For older versions there is an app called Dev Tools which contains the same settings. However, on low memory conditions the system can disregard those settings and terminate your application. Increasing the amount of memory assigned to the emulator might help.
Also, if you are re-launching your app from Eclipse, it will kill the previous process instead of gracefully terminating it.
I agree with milter!
onPause():
"If an activity has lost focus but is still visible (that is, a new non-full-sized or transparent activity has focus on top of your activity), it is paused. A paused activity is completely alive (it maintains all state and member information and remains attached to the window manager), but can be killed by the system in extreme low memory situations."
If you swap applications without pressing Back (press and hold HOME) then the OS is going to call onPause. When you return to your activity (press and hold HOME again) in onResume you should have all of your private variables preserved. But you can't control the user, right?!
if you anticipate that the user is going to leave your app and the OS calls your onStop you better save your data if you intend to resume where you left-off.
I have a Timer also, I need to save the elapsed time so when the user returns I can restore the data.
here is my example to save:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// Save UI state changes to the savedInstanceState.
// This bundle will be passed to onCreate if the process is
// killed and restarted.
savedInstanceState.putLong("elapsedTime", elapsedTime);
// etc.
}
And my code to restore:
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
elapsedTime = savedInstanceState.getLong("elapsedTime");
}
Place these methods inside of your class and you are good to go. Keep in mind that the string "elapsedTime" in my case is a KEY to the system and it must be unique. Use unique strings for each piece of data that you would like to save. For example "startClock", "ClockTextColor", etc...

Is the static safe in Android?

I use a single static class in my code that defines a static field which I'm reusing between Activity onStop/onStart invocations. Here's a scenario:
User clicks on "Authorize" button (static data is initialized)
Activity is stopped and web browser is called
Browser executes callback and Activity is restored (static data is reused)
At least one of my users reports the failure at step 3 which I cannot reproduce but which looks like reset of static data
Any suggestions?
That is not safe. Your process can be killed between onStop and onStart, so all static data will be gone. In fact your activity can even be killed before it gets to onStop. In your tests the process was not killed, but it was for the user. See the Android activity life cycle which has a nice flow chart showing the possibilities.
You need to store the data some other way, in prefs or database for example.
If this static data is related to activity which you have just stopped - you could use normal non static fields + onSaveInstanceState method.
#Override
protected void onSaveInstanceState(Bundle outState) {
// ... save your Serializable data here in outState bundle
super.onSaveInstanceState(outState);
}
The case would be:
you close your activity and go to browser (onStop is invoked)
system kills your application process (onSaveInstanceState is invoked
where you save data)
User navigates back to your activity (onCreate is invoked with
savedInstanceState parameter)
In most cases 2nd point will not occure. System can but doesn't have to kill your app process. When it doesn't - you will not get onCreate method but onStart and onResume methods and your fields will be unchanged.

Categories

Resources