I was looking at this page from "Android Programming" by big nerd ranch, and I was confused by the sentence beneath. It states that "when the activity is stashed, an activity object does not exist." This is confusing to me because when I open an app and press the home button, onPause() is called and the activity reaches a "Paused" state with its activity stashed in the OS, as shown in the figure. It must be still running in the background because onDestroy() is not called when I press the home button. In fact, when I open my task manager, I can see how the activity looked like when I pressed the Home button. What exactly do they mean by "activity object does not exist?" when it is clearly on paused state in the background?
THeir documentation looks to be a bit wrong and confusing. The official Android documentation doesn't talk about a "stashed" state, it would be the block "App process killed" in the documentation here
Basically, from the STOPPED state only, the OS may destroy your activity at any time. If it does this, is will call onSaveInstanceState. The Activity variable would then be invalid. At any time, it may then recreate a new Activity and call onCreate then onRestoreInstanceState on it, passing in the Bundle you save previously, to recreate the activity.
This is confusing to me because when I open an app and press the home
button, onPause() is called and the activity reaches a "Paused" state
with its activity stashed in the OS, as shown in the figure.
I would not say that this is "stashed". Your activity is alive and well, just paused.
It must be still running in the background because onDestroy() is not
called when I press the home button.
That's right. When you are hitting the home button you are saying to the OS "Hey I'm going somewhere else, but I'm not necessarily done with this activity."
If you instead hit the back button, then you'll see onDestroy called. Here you are saying to the OS "Ok I'm done with this activity, do with it what you want."
What exactly do they mean by "activity object does not exist?" when it
is clearly on paused state in the background?
The stashed state is entered when the OS needs to destroy your activity without you "telling" it to do so (i.e. hitting back button). This happens when your device goes through a device configuration change. A classic example is rotation. This also happens if the OS needs to free up memory. In this case onSaveInstanceState will be called to capture the state of your activity in a Bundle. The stashed state is then the preservation of this Bundle and the class name of your activity. The actual Activity object is marked for garbage collection. Then given just the Bundle object and the class name of your activity, the OS can create a new instance of your activity if the user returns to it.
One take away is that you don't have to implement onSaveInstanceState yourself unless there is specific information you want preserved. Some components of the view hierarchy will automatically add information about themselves to the Bundle because the super Activity.onSaveInstanceState will still be called. From the docs:
The default implementation takes care of most of the UI per-instance
state for you by calling onSaveInstanceState() on each view in the
hierarchy that has an id, and by saving the id of the currently
focused view (all of which is restored by the default implementation
of onRestoreInstanceState(Bundle)). If you override this method to
save additional information not captured by each individual view, you
will likely want to call through to the default implementation,
otherwise be prepared to save all of the state of each view yourself.
http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)
You can check the source here:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/app/Activity.java#1371
Related
I have three activities in my application.
From the first one i started the second, and from the second i started the third. When the process of my app is killed by the system and i launch it again i see only the last active activity is being created. And it seems the only one that gets Bundle object as a parameter to its onCreate method.
There are two things I am curious of:
If the activity at the top of the stack is the only one that gets its bundle, why each of my activities got their callback (onSaveInstanceState) called just before their onStop() method.
If only one of my activities can retain original state, what about the others? Did they lose all their state just because system decided to kill my app process? Should i restore them manually? What about views on them? (normally views get their state back without needing me to put something into the bundle and later restore if i am not mistaken)
The default behavior is this (tested on 4.1.1):
When you start a new activity, onSaveInstanceState of the previous activity is being called
When the system kills the app, the state of activities back-stack is being saved
When you restart the app, the last seen activity is being shown and its onRestoreInstanceState is being called
When you navigate back and pop activities from the back stack, state of each of them will be restored with a call to onRestoreInstanceState passing the bundle that was obtained from the first step above.
Therefore, the answers to your questions are:
All activities will be restored, but not at once - the last one seen is restored immediately, while the others will be restored when you navigate back.
All simple views (e.g. EditText) will automatically restore their state. In order for this to happen, you need to make sure that a) you did not override onSaveInstanceState or onRestoreInstanceState without call to super implementations b) the views that should be restored have unique IDs in view hierarchy
onStop is called on previous activity when you create a new activity, so you are saving the state! Your previous activity's onCreate will be called, if it's destroyed, so don't worry about recreating views. Are you sure app is killed? An app is not killed when it loses view. Android will keep it in memory as long as it doesn't need to free memory.
In an app I am developing, I have a custom view in which I need to save the instance state when either the screen configuration changes or the app is moved to the background. I implement onSaveInstanceState() and onRestoreInstanceState(Parcelable savedInstanceState) and in the case of screen configuration changes, the instance state is saved and restored perfectly. However, when the app is moved to the background, onSaveInstanceState() is not called. This means I have no saved instance state when the activity is brought to the foreground again. My question is then:
Why is onSaveInstanceState() not called when the app is moved to the background, and is it possible to make my view call onSaveInstanceState() when the app is moved to the background?
It occurs when you press the back button to leave the activity. –
Zero
When using the Back button to leave the application the onSaveInstanceState method is not called by the Android system, this is expected system behavior. The back button is meant to stop your app (Read: activity).
The Back button finishes your Activity and there is no need to save app state. But if there is reason to to this then you should override the onStop method for example and save data to the file system there.
Sources:
http://developer.android.com/training/basics/activity-lifecycle/recreating.html
(Recomended read)
There are a few scenarios in which your activity is destroyed due to
normal app behavior, such as when the user presses the Back button or
your activity signals its own destruction by calling finish().
As per my knowledge, when app enter into background, on Activity it calls onPause() method. So please try by moving your code to onPause() method.
In Andoid Acitivity life cycle, why does an activity go through onPause() before going to onStopped()? Why can't the state go directly to onStopped()?
Paused and stopped are related but different states. From the point of view of the user, a paused activity cannot be interacted with, but may still be visible (e.g. if it has called a different Activity as a dialog). A stopped activity is guaranteed to be not visible at all (the uses is in another Activity or even a different app).
Of course, stopped implies paused, but the reverse is not the case.
From the official documentation.
If an activity in the foreground of the screen (at the top of the stack), it is active or running.
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 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.
An even more detailed explanation is given in the Managing the Activity Lifecycle article in the Training section of the Android Developers site.
Refer to the documentation on Activities:
http://developer.android.com/reference/android/app/Activity.html
onPause()
"Called when the system is about to start resuming a previous activity. This is typically used to commit unsaved changes to persistent data, stop animations and other things that may be consuming CPU, etc. Implementations of this method must be very quick because the next activity will not be resumed until this method returns.
Followed by either onResume() if the activity returns back to the front, or onStop() if it becomes invisible to the user."
Note: I would say "resuming another activity" instead of "resuming a previeous activity".
onStop()
"Called when the activity is no longer visible to the user, because another activity has been resumed and is covering this one. This may happen either because a new activity is being started, an existing one is being brought in front of this one, or this one is being destroyed.
Followed by either onRestart() if this activity is coming back to interact with the user, or onDestroy() if this activity is going away."
Because the documentation says so :-)
And it makes sense. The app is open and the user pressed the home button: onPause() gets called. After a while the system needs the memory and closes the app: onStop() gets called.
Because, onPaused() is executed when your App is rotated or a Dialog is open.
onStop() when your App is not in the screen, so is necesary that this two points in the lifecycle to the user or programmer can identify what action is executed.
I have an app in which I programmatically create an EditText view. I assign an ID to that view using setId()
myEditText.setId(100);
so that Android automatically saves that object's state when pausing/stopping the app (as I was advised to do here). It works in these cases:
(1) When I leave the app using the "Home" button: if I then come back to the app, the object's state (displayed text) is restored, as expected.
(2) On a screen orientation change (which involves Android automatically destroying the activity and restoring it through a Bundle). The object state is also kept.
However, it doesn't work in this case:
(3) When I leave the app using the "Back" button: if I then come back to the app, the EditText object is empty.
Any explanation as to why this happens? Does Android really distinguish between leaving the app with "Home" and with "Back"? According to the documentation, the object's state should be automatically preserved, through a Bundle, even when the activity is destroyed. And that clearly happens in case (2). But not in case (3)!
If this is normal behaviour, how could I have the app's state automatically saved and restored when the user presses "Back"? I know I could use the SharedPreferences for that, but I'd rather have Android do that automatically, just as it does in cases (1) and (2).
This happens at least in Android 4.0 and 4.2 (I haven't tested others).
You really should study activity life cycles as there are many many ways to solve the problem. Since your activity is typically pulled off of the stack and destroyed when you navigate back one quick but not necessarily the best way is to make sure your activity flagged as singleTop or singleInstance in the manifest that way it is not pulled off of the stack and recreated when you navigate back and forth. You could also use the singleton Application class. Or pass the text back and forth as params. Or use a database. Or use MVC or some other programming paradigm that will allow your views to be destroyed and recreated with out the data populating them going with it. Lots of "or's". Study activity life cycles and then look at how you have your application architecture setup already and choose the method that will work best for you.
http://developer.android.com/training/basics/activity-lifecycle/index.html
http://developer.android.com/guide/components/tasks-and-back-stack.html
I think I found the explanation. I only needed to read the doc more carefully (thanks to #lentz for one of the links); see here and here:
When your activity is destroyed because the user presses Back or the activity finishes itself, the system's concept of that Activity instance is gone forever because the behavior indicates the activity is no longer needed.
If the user presses the Back button, the current activity is popped from the stack and destroyed. The previous activity in the stack is resumed. When an activity is destroyed, the system does not retain the activity's state.
The above explains behaviour (3) in my question.
However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actual Activity instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the "instance state" and is a collection of key-value pairs stored in a Bundle object.
The above probably explains behaviour (1) and (2).
What I don't see is why the user pressing Back should be interpreted as "the activity is no longer needed" ("its state needs not be preserved"). But that's a different matter.
The Android documentation regarding the recreation of an activity states:
When your activity is destroyed because the user presses Back or the activity finishes itself, the system's concept of that Activity instance is gone forever because the behavior indicates the activity is no longer needed. However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actual Activity instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed.
To clarify: does this mean that onSavedInstanceState becomes null when the user presses the back button?
Thanks
Unfortunately, it seems yes... Android follows many bad design decisions :(