Note: Even if the system destroys your activity while it's stopped, it still retains the state
of the View objects (such as text in an EditText) in a Bundle (a blob
of key-value pairs) and restores them if the user navigates back to
the same instance of the activity (the next lesson talks more about
using a Bundle to save other state data in case your activity is
destroyed and recreated).
the same instance of the activity
how it could be exact instance when it is destroyed and recreated , isn't it going to be a new memory block (another instance ), could anyone help me to clear this point ?
Unfortunately the documentation isn't very clear in a lot of areas. This is one of them.
In theory, if Android were to destroy your activity while it is stopped, then it would call onDestroy() and the GC would reclaim the memory. In actual fact, Android never destroys individual activities to free up memory. What it actually does is it kills the entire OS process instead. In this case, onDestroy() never gets called on any of the activities in the process. The GC doesn't bother to clean up anything, because the VM (Virtual Machine) just dies immediately along with everything else in the process and the entire amount of memory in use by the process is reclaimed by the operating system.
When your user navigates back to your application, Android creates a completely new process for your application and then it will create a new instance of your activity. In this case it will, of course, get a completely new block of memory for the instance. You will also see the constructor get called and onCreate() will also be called. However, since Android saves the state of the activity's views, that state will be restored by the activity by the call to super.onCreate().
There are a few cases where Android will destroy an instance of an activity and create a new one automatically. This is done, for example, during a configuration (ie: orientation) change. In that case, Android calls onDestroy() on the old instance and creates a new instance of the activity. The new instance gets the saved state of the old instance and can therefore restore the state of the views as able. In this case, since a new instance is being created, it will, of course, have a different address in memory.
Once a component is destroyed, it is effectively dead and the memory it is using can be reclaimed by the GC. Android will never reanimate a dead instance.
Hopefully this clarifies the situation for you.
EDIT Add more details
Android keeps track of tasks and the activities in those tasks in its own internal data structures. For each activity, it keeps a copy of the Intent that started the activity, the most recent Intent sent to that activity and it keeps a Bundle containing the saved state of that activity. Android calls onSaveInstanceState() on an activity to give the activity a chance to save anything that it will need to restore the activity if Android decides to kill it. The default implementation of onSaveInstanceState() saves the state of all the activity's views. Your implementation needs to save anything else that the activity will need to restore itself if it is killed and recreated by Android (for whatever reason). Your activity's private member variables and static variables are not automatically saved and restored in the Bundle, so if you need these to be able to recreate your activity properly you must save them yourself using the Bundle provided by onSaveInstanceState(). Static variables will stay around for the lifetime of the application's process, but since Android can kill the process (to reclaim resources) at any time, without warning, you also cannot rely on static variables always having the expected values (in the case where Android has killed and recreated your process).
If your activity (or process) is killed by Android and the user later navigates back to your activity, Android uses the information it has in its internal data structures to create a new instance of the activity. It then calls onCreate() on the new instance passing the saved instance state Bundle as a parameter. This can then be used to restore the activity to the state it had before the original instance was killed. Android will also call onRestoreInstanceState() after calling onStart() so that you can also do your restoration of state in that method if you don't want to do it in onCreate().
Note: Remember that anything you want to save/restore in a Bundle must be either a primitive (int, boolean, etc.) or it must implement Serializable or Parcelable.
The documentation of onSaveInstanceState() contains some useful information about this as well.
The state is stored, but the activity is destroyed to save memory. So, when the user is in an Activity A and goes to the Activity B, the Activity A is destroyed, but the state is stored. When the user presses the back button, the state will be loaded and everything will be restored.
The system does it when the activity call onStop and onStart. I didn't make any test, I'm just saying what I understood.
Related
Having a bug which relate to the os kill/restore the activity (or the app?).
After some debug find if set don't keep activities and set Background process limit to no background process will cause different behavior.
saw this post, but it does not answer the question here.
Here is what observed:
In the application it will start dagger component and it maintains some app scope singleton object, and in activity A (the default launch activity) it will by user's action to launch activity B, in B it creates and hosts a fragment. There will be some data stored in the app scope singleton object to work with the fragment.
In case of only have the don't keep activities set, when minimize the app the activities onDestroy() is called, and when re-open the app it restores the last active activity (say the user opened the activity B, the B will be re-created with the fragment restored with the savedInstanceState). In this case the app scope singleton objects managed by the dagger are still alive, so the state is completely restored to what it was before minimized the app.
But if have both don't keep activities and set Background process limit to no background process, then when minimize the app, the activity's onDestroy() is not call (only call up to the onStop()).
The behavior change is at this time if re-open the app, it will start from application onCreate(), and recreate the dagger's component. So the state before minimize the app is not re-stored.
But the os seems still remembers the last activity is B and the B's
onCreate(savedInstanceState: Bundle?)
is called with savedInstanceState having the data saved when minimizing the app, so does B's fragment.
And it is a messed up, it has data from the savedInstanceState, but the app scope singleton objects are fresh ones that do not have the data to work with the ones from the savedInstanceState.
Anyone knows in this case where is the savedInstance saved, and why although the application seems to be recreated but still the last activity (not the launching activity) is re-stored?
The savedInstanceState bundle is explicitly intended to do as you describe. Regardless of whether a background activity is destroyed to conserve memory (e.g. "don't keep activities" is turned on) or whether an entire app process is killed to conserve memory (e.g. "background process limit" is zero), the framework will provide your app the ability to save state information into the savedInstanceState bundle and will subsequently return that state information to you in future calls to onCreate().
The only situation in which savedInstanceState will be null is the very first time your activity starts up. It doesn't matter whether your app's process is killed or not; if your activity is restored you will receive a non-null savedInstanceState bundle.
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 my application, an activity A calls the activity B (via an explicit intent). Lifecycle, B may get killed and recreated etc.
When B is called (from A) it initializes some things. But when recreated it needs to just pick up from where it left. It will finish() some time, then I'm back to A, which might later call B again...
B needs to save a very small amount of data, usually just a few int values. (The rest that I need to restore it is in getIntent() which seems to be still there after the activity has been killed and recreated.) I've heard that onSaveInstanceState and onRestoreInstanceState are expensive and that they are not guaranteed. Plus, I don't need to save the state of views. (For that reason, should I override these two methods with blank ones, preventing the parent ones to get called?)
What's the efficient way to store just a few int values? Should I store the values as static fields of the activity B itself, or of the application itself, or of another class written just for that?
Also, how does the activity B know whether it's been recreated rather than called?
I've heard that onSaveInstanceState and onRestoreInstanceState are expensive and that they are not guaranteed.
They are not particularly expensive. And, for your scenario, they will be used most of the time. The exception would be if your task is not being restarted, such as:
the user got rid of your task by swiping it off the recent-tasks list
at least pre-Android 5.0, your app has not be run in ages and falls off the end of the recent-tasks list
the user force-stops you from Settings
But in those cases you will not be restarting at B, since I assume B is not your launcher activity.
For that reason, should I override these two methods with blank ones, preventing the parent ones to get called?
Not unless you are experiencing actual problems, not just rumors of hints of potential problems.
What's the efficient way to store just a few int values?
For your case, probably use the saved instance state Bundle.
If you cannot afford to lose those values even if your task goes away (and you will not be restarting at anyway), store them in a file, database, or SharedPreferences.
Should I store the values as static fields of the activity B itself, or of the application itself, or of another class written just for that?
No, because none of those will work. You specifically state that your concern is "Lifecycle, B may get killed and recreated etc.". Part of that lifecycle is that your process may be terminated, and in that case, static data members go "poof".
Also, how does the activity B know whether it's been recreated rather than called?
See if the saved instance state Bundle passed into onCreate() is not null. Or, see if a retained fragment exists, if you're using one of those.
When OS will completely forget the state about an Activity and all information relate to an Activity ( any records about an Activity) ?
In other words when it will make Bundle to be a new instance?
I found the below explanation but it does not explain this "Killing bundle" point?
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/Activity.java#Activity.onSaveInstanceState%28android.os.Bundle%29
Called to retrieve per-instance state from an activity before being killed so that the state can be restored in onCreate(android.os.Bundle) or onRestoreInstanceState(android.os.Bundle) (the android.os.Bundle populated by this method will be passed to both).
This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(android.os.Bundle) or onRestoreInstanceState(android.os.Bundle).
Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when an activity is being placed in the background or on its way to destruction, or onStop() which is called before destruction. One example of when onPause() and onStop() is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState(android.os.Bundle) on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause() is called and not onSaveInstanceState(android.os.Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(android.os.Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.
The default implementation takes care of most of the UI per-instance state for you by calling android.view.View.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(android.os.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.
If called, this method will occur before onStop(). There are no guarantees about whether it will occur before or after onPause().
There are many cases where this could occur:
Here is a small topic on reference mentioning different
use-cases about recreating activities.
If you want more control over the application life cycle:
Take a look a the application object for more information.
Personal advice:
I think you should not care about when/what, This is something the
android system will manage for you, your code should be optimized to
handle all usecases (when the bundle will be null or not). A bundle
should only be used to handle state-keeping, if you want to
persist data you should take a look at fileI/O, SQLlite or shared
preference.
Quick question.
I have an activity that calls my save function at the proper moments, onPause and onSavedInstance.
The onPause happens if the activity leaves the foreground and the onSavedInstance before being killed.
My activity saves the state perfectly if the user presses the "home" key but if he presses
the "back" key the onPause still happens and consequently some fields are saved in
the activity class attributes.
The problem is that when the User goes back to the activity the onCreate is called meaning (for what I understood) that the class is instantiated and therefore its attributes are null again.
If the onSavedInstance was called when the back key was previously pressed I could use it to save the activity state but it's not.
So my question is, when the user presses the back key how can I save the activity sate withou using sqlite, file saving and other persistance methods?
When the user presses the BACK button, your foreground activity is destroyed. That activity will be called with onPause(), onStop(), and onDestroy(). That activity object will then be garbage collected (unless you introduced a memory leak).
onSaveInstanceState() will be called periodically if there is a chance that the activity will be destroyed shortly but in a way where the user might be able to navigate back to it. The prominent case for this is during a configuration change, such as rotating the screen.
What you should be doing in onPause(), if anything, is persisting data using "sqlite, file saving and other persistance methods". Once onPause() is called, there are no guarantees that this activity will stick around, or that your entire process will stick around. Anything you value, therefore, should get written out to persistent storage.
The "state" for onSaveInstanceState() would be things that affect the activity's UI but are not part of the persistent data model. Much of this is handled for you automatically by Android's built-in implementation of that method (e.g., the text in an EditText), but you can add your own information to the Bundle if you wish. However, your instance state is not your data model, so anything you want to stick around needs to be written out to persistent storage.
If your concern is performance, you are welcome to cache data in static data members/singletons, assuming you do not introduce a memory leak. But, again, once onPause() is called, your process may be terminated at any point in time. Your static data members can only be a cache; your data model must be something persistent.