Android onSaveInstanceState Bundle - android

When android kills your process due to system constraints it gives you the ability to persist data across processes by storing the data in a bundle. Where is this bundle saved if your process is killed? Which process does it live in? Where in memory does it live in? Does it live in kernel memory?

Kernel memory is a protected memory space where only critical code like the kernel code reside. This is to prevent interference of data from the user and that of the kernel and also for other performance and design reasons. Kernel memory will persist through a reboot.
The data being persisted in the Bundle is passed in the method of the onSaveInstanceState() - before the user leaves the Activity not before the Activity gets destroyed. This likely means that the memory is not written in a static memory area like the memory card. The Bundle will indeed be persisted but in a more dynamic way.
While this question has not been directly answered, it looks like this is memory on the RAM. When you look at the performance of Android devices, the more recent devices with more RAM seem to be capable of holding on to apps for much longer
From the official documentation at http://developer.android.com/training/basics/activity-lifecycle/recreating.html, it is understood that this memory is tracked by Android itself.
By default, the system uses the Bundle instance state to save
information about each View object in your activity layout (such as
the text value entered into an EditText object). So, if your activity
instance is destroyed and recreated, the state of the layout is
restored to its previous state with no code required by you. However,
your activity might have more state information that you'd like to
restore, such as member variables that track the user's progress in
the activity.
To save additional data about the activity state, you must override
the onSaveInstanceState() callback method. The system calls this
method when the user is leaving your activity and passes it the Bundle
object that will be saved in the event that your activity is destroyed
unexpectedly. If the system must recreate the activity instance later,
it passes the same Bundle object to both the onRestoreInstanceState()
and onCreate() methods.
Also keep in mind that this Bundle gets destroyed when the device shuts down. Android might have a specially designed memory area for this but either way it is somewhere in the RAM.

Related

How do we retrieve saved state if the Activity is killed by OS?

So, as far as I have understood, once the App is not destroyed and is in the background; if the OS requires more memory the OS kills the app but saves the state (onSaveInstanceState). And when we re-open the app, it would seem like we are facing our previous activity but it actually has been destroyed and created again. If my interpretation is correct, how does the App retrieve the saved state? Does it store it in memory? For how long are we able to retrieve the saved state?
If my interpretation is correct, how does the App retrieve the saved
state?
From the Android documentation
If the system destroys the activity due to system constraints (such as
a configuration change or memory pressure), then although the actual
Activity instance is gone, the system remembers that it existed. If
the user attempts to navigate back to the activity, the system creates
a new instance of that activity using a set of saved data that
describes the state of the activity when it was destroyed.
Regarding your second question - it's the implementation detail how the OS does it and it's actually shouldn't worry us :). What's important is that it should do it reliably.
The system will keep the saved state as long as the user doesn't press Back or finish() of your Activity is not called.
The OS stores it. It calls onSaveInstanceState to let you generate a Bundle it stores, and will call onRestoreInstanceState to let you restore yourself from the state. How the OS stores it doesn't matter- maybe it saves it in RAM, maybe it serializes it to disk. What is assured is that if you go back to that activity you will be passed a Bundle object with the information you previously filled in. You do not need to retrieve the state- if it exists it will be passed to you.

Does Fragments with setRetainInstance(true) survive process shutdowns?

Considering this scenario: If I created an activity and it moves to the background and this activity contains a Fragment which is set to setRetainInstance(true) then the Android OS might at some point still decide to shut down the activity's hosting process in order to free memory.
Then the Activity's state is saved via onSaveInstanceState(Bundle) where - as far as I understood - the related Bundle is written and to the file system to survive the process shut down. (thus the requirement of objects in the bundle being Serializable). Later, the applications state can be retrieved in a new process via onRestoreInstanceState(Bundle).
In contrast, my Fragment is allowed to contain variables which are not necessarily Serializable. Therefore, I figured, the Fragment cannot be stored on disk like the Bundle is. So what happens to my fragment when the process gets killed?
I was wondering about this reading the developer's guide (http://developer.android.com/guide/components/processes-and-threads.html):
A process holding an activity that's not currently visible to the user
(the activity's onStop() method has been called). These processes have
no direct impact on the user experience, and the system can kill them
at any time to reclaim memory for a foreground, visible, or service
process. Usually there are many background processes running, so they
are kept in an LRU (least recently used) list to ensure that the
process with the activity that was most recently seen by the user is
the last to be killed. If an activity implements its lifecycle methods
correctly, and saves its current state, killing its process will not
have a visible effect on the user experience, because when the user
navigates back to the activity, the activity restores all of its
visible state.
I understood the above killing such that the VM instance is shut down and the state of the process is written to the file system (here comes the Bundle into play). Later the bundles are read for resuming the process. Since the retaining of fragments is not concerned with life cycle methods and since I would not know how to retain e.g. a pointer to a network connection (you should of course never have such a pointer in a fragment anyhow), I was wondering if the fragments are still restored if the process is shut down in the meantime. I concluded that they surely needed to be recreated and that the life cycle methods are therefore to be preferred over setRetainInstance(true) whenever possible.
Does this assumption make any sense?
Sounds like you're mixing up two concepts here.
Saving state across Configuration Changes does not involve serialization. If you request setRetainInstance() for a Fragment then that means it will fully stay in memory and not be re-created only for configuration changes. A similar mechanism is available for Activity objects but they need to explicitly define an Object which is going to be saved. This works via Activity.onRetainNonConfigurationInstance(), not via onSaveInstanceStae().
The other mechanism involves serialization and possibly (maybe not always, not sure) file system I/O to be able to reproduce state information even if an Activity/Fragment is destroyed (which happens independently of its hosting Process, btw). This works via Activity.onSaveInstanceState() and Fragment.onSaveInstanceState().
Of course, you can use the second mechanism for the purpose of the first, thus slowing down the way your app deals with configuration changes. Depending on your internal state, the slowdown could me marginal of significant.
Regarding your questions.
"My Fragment in contrast, is allowed to contain variables which are not serializable." Well, the same is true for your Activity. It can contain non-serializable objects which can be saved across config changes as described above.
"the fragment cannot be stored to disk when a process is shut down and must be recreated when an activity was restored." No, both mechanisms are available for both object types.
Hope I could contribute to clarifying this a bit.
Edit after your first comment.
Regarding the comment:
"onRetainNonConfigurationInstance is deprecated": Yes. I mentioned it for demonstration purposes because of a specific wording in your question. Also, with Android 2 devices having a 46% market share as per today (official Google figures), this method will definitely stay around for a very long time, deprecated or not.
"My main concern is about what will happen to the fragment instance when my hosting process is killed and removed from the memory": Your fragment instance will be removed from memory and there's of course no way it is restored as-is with its complete internal state automatically. This is only done when you setRetainInstanceState in the case of config changes. (But note that this relates to the Instance, in other words, the full object.)
Regarding your edit:
Once more, yes, your Fragment's Bundle will be stored and restored to/from the Bundle regardless of setRetainInstanceState if you use Fragment.onSaveInstanceState() for this purpose, for everything that makes sense.
It is not true that "all of its visible state" will be saved as the text you refer to claims; for example, the visibility attribute will not be saved. Whether that's supposed to be a bug or a feature I don't know, but it's a fact. But this is only a side remark; UI elements will save most of their relevant state.
"the state of the process is written to the file system": No! The state of objects which are able to save their state to a Bundle and actually implement saving their state will be saved in a Bundle, this means that you must provide such information yourself if you want your Fragment to save some state information. Also, again: No, this does not only relate to killing the process but also to deleting Activity and Fragment objects which are not visible; like the last Activity shown -- the Process may well stay alive.
"bundles are read for resuming the process": No, the Bundle will be read to pass it to the re-construction of Activity and/or Fragment objects, there is nothing done automatically in this process (except library objects which save their state also restore their state), but Android does not "resume" the "Process" from these Bundles.
"Since the retaining of fragments is not concerned with life cycle methods": Again, I think you're mixing up the two concepts. The "retaining" of a Fragment is only performed upon configuration changes _IF_ you request it via setRetainInstance, but we're mostly talking about the re-creation of Fragment objects from a Bundle here, which does involve the life cycle methods as documented by Google.
"I would not know how to retain e.g. a pointer to a network connection": Again, this must be a statement based on your mix-up. Of course you can keep a reference to a network connection upon config change (as requested per setRetainInstance) because when that happens, everything is simply kept in memory. Also, even if your Fragment gets deleted (because it became invisible) and your process is still there (because it shows the next Activity), you can (and should) keep references to objects which are expensive to re-create, such as a network connection, in your Application object, which exists as long as your process lives (more or less). It is only when your whole app is killed by Android that you lose everything, but the serialization we're discussing happens much more often.
Your conclusion:
I concluded that they surely needed to be recreated and that the life cycle methods are therefore to be preferred over setRetainInstance(true) whenever possible. Does this assumption make any sense?
Unfortunately not, since you are mixing up completely independent concepts.
I'll give it a final try:
You will want to keep a network connection reference which you need throughout your app in your Application object because it would be a lousy user experience if you created it from scratch on a regular basis throughout your app.
Your Application object will only die if Android kills your app.
Your Activity and Fragment objects will be deleted from your app regularly when the user moves forward within your app.
When the user presses "back", Android will re-create Activity and Fragment objects from Bundles using lifecycle methods. Saving something in a Bundle makes sense if you have expensive computations to do to re-create the internal state. You can live without the Bundle mechanism because Android will always save the Intent so if you don't do anything then you'll start without saved state.
When a configuration change occurs, Android lets you optimize user experience by keeping objects in memory across the config change. Here, Activity life cycle methods get involvwed and it's up to your implementation to use the saved data effectively. For Fragments, this is where setRetainInstance' comes into play: YourFragment` will survive the config change in memory if you set it.

Activity Recreation on Android

I am reading the official Android tutorial on Managing Activity Lifecycle. Please have a look at the following points as I quote.
(Normal app behavior)
"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."
(System induced destruction)
"If the system destroys the activity due to system constraints (rather than normal app behavior), then althought 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 a collection of key value pairs stored in a Bundle object. By default the saved information is about the activity's view hierarchy, such as the text entered in a widget, the scroll position, etc. To save additional state information for your activity, such as member variables, you must implement onSaveInstanceState() and add key-value pairs to the Bundle object."
Please see if I can sum up correctly from the above:
The system is resource conscious and can destroy an activity to recover memory. If the system does that then it leaves an option of recovering the destroyed activity to its previous state if needed later.
State = view-state (default, done automatic) + member-variable-state (manual, up to the programmer).
My question is, if the system is destroying an activity in order to free some memory, then doesn't it defeat the purpose if it allows us to keep a "copy" of the activity so that the activity can be restored
to the point exactly as it was prior to destruction?
No, it does not defeat the purpose:
A Bundle is optimized for being serializable, so the Android framework could just write it to disk.
The data you need to represent your state is much smaller than the amount of RAM you need for a running copy of your app. For example, if you show an image to the user, you'd probably just have to save the image's location, not all of its data. Also, every Android app is a whole new Dalvik VM, so if all the activities are from different applications, killing an activity means one Dalvik VM less – and every Dalvik VM probably needs a relatively big amount of RAM.

Where is the bundle of onSaveInstanceState saved?

I would like to know where the bundle "outState" of the method onSaveInstanceState(Bundle outState) is stored.
Is it stored in memory or in the device storage?
I am concerned about the security of the data which is stored in the bundle.
To store data only for application lifetime (ie temporarily), use the onSaveInstanceState(Bundle) activity event
This data will only be held in memory until the application is closed, the data will be available any time that this activity starts within the current lifetime of the application.
Explanation: if data is stored here by activity A then the application shows a different activity or rotates the screen (hence closing A) and then returns to A the data can be retrieved to populate the controls. However if the application is closed and opened again the data will be gone and the controls will revert to their default values.
Example of use: storing text typed in by user and selections making up an order, blog entry, message, etc...
Note:
It’s important to notice that only the Activity is destroyed and recreated, not your whole application! An Android application can consist of many Activities, Services and ContentProviders! If the application is closed (for example by pressing the “Back” Button, then all values will be gone. savedInstaceState is only there to preserve data temporary when an Activity is destroyed/recreated, not the application itself.
If you want to preserve data permanently, you need to save it either as Preferences or in a ContentProvider/database.
Here is a detailed answer for where the outState Bundle data is saved:
...Bundles are an IPC mechanism, so it's not going to the filesystem. But now there's a P involved – which process is it? And what is that process doing with this data? And do I need to be worried about it? It turns out that these instance state bundles are stored in the Activity Manager service. This service is implemented under the package com.android.server.am in the Android source code. Recall that Activities are stacked one on top of another and that Android calls these stacks “Tasks”... Each of these tasks is represented internally with an object of class TaskRecord. This class contains an array of ActivityRecord objects, each of which manages the state of an Activity. ActivityRecord contains a member of type Bundle named icicle. This icicle-bundle is the saved instance state and it is actually stored in the memory space of the Activity Manager service.
Source: https://www.linkedin.com/pulse/android-onsaveinstancestate-bundle-secret-safe-daniel-pietsch/
The documentation has been updated and indicates precisely that the state is serialized to disk:
Saved instance state bundles persist both configuration changes and process death, but are limited by amount of storage and speed because onSavedInstanceState() serializes data to disk.
You can also found a table comparing the differents approches to preserving UI state
Source: https://developer.android.com/topic/libraries/architecture/saving-states
I don't think there's any way that any malicious background process can get at the bundle data of your application. It is not documented how Android treats the Bundle data. It may or may not be written to disk in the event that your app is cleaned, while backgrounded. However, given that we don't know whether or not this data is saved to disk, and if it is, given that we have no clue where, and almost certainly don't have read access to that part of the disk, I wouldn't worry about some third party process being able to recover that data.
Consequently I'm not clear what you might think the exposure is. Though I may be missing something.
However, in answer to your question, it is absolutely in memory while your app is alive, and if your app is backgrounded it may or may not be written somewhere hidden, but we dont' know because Google hasn't told us.
It's destroyed along with the application when the memory is collected.
My guess would be in memory, but the best way to protect your data would be not to trust the system and encrypt it. Never trust the client (in this case the client being the OS).
EDIT:
To be clear, I'm not saying encrypt the bundle. Rather I'm saying that any sensitive data should not be put into the bundle. If you must put custom data in the bundle, then encrypt it.
But ultimately you should keep as little sensitive data on the client as possible. This is the same reason a e-commerce site would only show the last 4 digits of a credit card.

Android Bundle = .NET Session[]?

The description of Android Bundle says:
Bundles are Android's way of maintaining and passing program state. The system is constructed around the assumption that any program (characterized by a collection of activities) may be preempted at any time, and if the system resources are low, may be subsequently killed. To handle this difficulty, a Bundle called an icicle is saved whenever the activity is preempted.
Like with a hash table or dictionary, items added to the Bundle are given a key which is later used to retrieve the data (using data-type specific methods like getCharacter(String key) and getParcelable(String key)). When you are creating activities, be sure to save any important state information into the icicle given to onFreeze so that you can restore your program in onCreate if it is killed while suspended.
Is Bundle the same as Session[] in .NET?
Not exactly, no.
In .NET the you can access the State object from anywhere at any time, and you will be pretty sure that objects that you have placed inside of it will still be there when you are calling it. The session expires though, and unless you have saved anything in your viewstate, that data becomes lost.
Android uses Bundles because of the nature of the OS, it will keep your application "alive" in the background where state is preserved, but if it deems that your application is idle and that it can be killed, it will kill your app to free up resources. Before your app process is killed it will call onFreeze() (now renamed to onSaveInstanceState()) which will give you the opportunity to save data to the Bundle that will be passed to onCreate the next time your application starts up (so you can initialize your screen again).

Categories

Resources