Trying to understand what is the difference with using the ViewModel to keep some of the state of the activity or fragment, and saving them with the savedInstanceState bundle.
Got a impression that the ViewModel instance is kept alive when the activity/fragment is destroyed by os in the case like configuration change so that when os recreate the activity/fragment could get the data from the ViewModel instance which is still valid.
Does it apply to minimize the app and re-open it?
Did some test, seems minimize the app and re-open the app, the os will recreate the activity/fragment with the stavedInstanceState bundle in the onCreate() not null (whatever is saved when the onSaveInstanceStae() is called). But the ViewModel has been cleared so a new instance is created without previous ones data.
Does it it mean although is in this case the os can retrieve the saved instance state and pass to activity/fragment's onCreate(), but the ViewModel has to be a new instance without previous instance's data, or the viewModel needs do to some extra step inorder to store/restore the data cross the instances?
If someone is still looking to understand difference between onSavedState vs ViewModel here is the detailed explanation:
onSavedInstanceState : Primary usage of onSavedInstance was not to handle orientation change but to provide a mechanism to retrieve data if app/activity is destroyed by Android System. Example case when app is in background and Android system decide to kill this as it needs memory for some other high priority process then in this case before activity is destroyed onSavedInstanceState will be called.
onSavedInstanceState only stores the Parcelable data, that provides hint to restore the state for the user when activity restarts. It saves data in System server that is a separate process.
onSavedInstanceState has data limit. Only small amount of Parcelable data can be saved.
While for ViewModel
ViewModel object is part of Applications process memory and hence it is able to survive configuration changes. Once a process dies, ViewModel goes away and all the saved state will be lost. Hence when activity restarts, ViewModel has nothing in it.
It works as a cache for heavy objects.
There are no restrictions in ViewModel.
Important: Always remember ViewModel and SavedState work together. They are not replacement or alternative to each other.
A good explanation (and a solution to your problem) can be found in this blogpost.
TLDR: the viewmodel is hosted inside a persisted fragment, which gets recreated together with the hosting activity.
From: Kristin Marsicano's Book “Android Programming: The Big Nerd Ranch Guide, 4th Edition.” :
ViewModel vs Saved Instance State
While saved instance state stores an activity record across process death, it also stores an activity record across a configuration change. When you first launch the activity, the saved instance state bundle is null. When you rotate the device, the OS calls onSaveInstanceState(Bundle) on your activity. The OS then passes the data you stashed in the bundle to onCreate(Bundle?).
ViewModel really shines when you use it to orchestrate dynamic data for the activity
ViewModel makes continuing a download operation across a configuration change simple. It also offers an easy way to keep data that was expensive to load in memory across a configuration change. And, as you have seen, ViewModel gets cleaned up automatically once the user finishes the activity.
ViewModel does not shine in the process death scenario since it gets wiped away from memory along with the process and everything in it. This is where saved instance state takes center stage. But saved instance state has its own limitations. Since saved instance state is serialized to disk, you should avoid stashing any large or complex objects.
lifecycle-viewmodel-savedstate is a new library that was just released to allow ViewModels to save their state across process death. This should alleviate some of the difficulties of using ViewModels alongside saved instance state from your activities.
Use saved instance state to store the minimal amount of information necessary to re-create the UI state (for example, the current question index). Use ViewModel to cache the rich set of data needed to populate the UI in memory across configuration changes for quick and easy access.
When the activity is re-created after process death, use the saved instance state information to set up the ViewModel as if the ViewModel and activity were never destroyed.
As of this writing, there is no easy way to determine whether an activity is being re-created after process death versus a configuration change. Why does this matter? A ViewModel stays in memory during a configuration change. So if you use the saved instance state data to update the ViewModel after the configuration change, you are making your app do unnecessary work. If the work causes the user to wait or uses their resources (like battery) unnecessarily, this redundant work is problematic.
One way to fix this problem is to make your ViewModel a little smarter. When setting a ViewModel value might result in more work, first check whether the data is fresh before doing the work to pull in and update the rest of the data.
Related
Now with the View model as we can handle the configuration changes and manage the UI data, So the onSaveInstance() I feel now if of no use for me.
Like earlier we used to store smaller data onSaveInstanceState() and used to restore it during configuration, now using view model we can easily get the updated data. So could you please tell me whats the actual usage of onSaveInstanceState() and onRestoreInstancestate() now if we are using ViewModel.
Could you please tell me the usage of it in the current case with ViewModel
Data can still be lost when Android OS decides to kill your application process due to memory constraints and then later re-create it. This is different from Configuration changes.
ViewModel successfully saves your data from the configuration change (Activity/Fragment Recreation), but it won't be of much help in case of Process Recreation.
In order to persist data even in case of Process Recreation, you will need to use onSaveInstanceState().
However with recent updates, by means of SavedStateHandle ViewModel directly allows you to save instance state, which will automatically survive process death/recreation. This implies, you no more need to use onSaveInstanceState of your Activity/Fragment to persist data across process death, just use SavedStateHandle in your ViewModel, it will act the same.
https://medium.com/google-developers/making-loading-data-on-android-lifecycle-aware-897e12760832
The article mentioned above states that
Loaders survive Configuration changes.
So the data I just retrieved will be available after device rotation.
It also tells that
Loaders don't stay around forever
They will be automatically cleaned up when requesting Activity or Fragment is destroyed. So if an Activity is destroyed when a device rotates which implies loaders being cleaned up how can I retrieve data from Loader?
The latest version of LoaderManagerImpl uses the ViewModel internally. The ViewModel class is designed to hold and manage UI-related data in a life-cycle conscious way. This allows data to survive configuration changes such as screen rotations.
Therefore, you can retrive data from Loader when your activity is recreated. If your activity is permanently destroyed when you click the Back button, the Loader is destroyed with it.
Android 7.0 start throwing TransactionTooLargeException while restoring instance state if the parcel size is over the limit.
But my application have to save the loaded content which has no size limit.
So it's very easy to cause this error.
Is it safe to save instance state in Application instance?
Will it be deleted while the Activity is in background?
Is there a way to save large data without throwing this exception?
To answers these questions one by one :
Is it safe to save instance state in Application instance?
Not if want to retrieve that data after your app's process has been killed by the OS and later restored. For that, you need to persist data to disk somehow. There are always two parts to properly handle saving state : handling configuration changes (like rotations) which do not involve process death and actual process death and restoration. Putting state in the Application instance (or any singleton) solves the first problem but not the second.
Will it be deleted while the Activity is in background?
Depends what you mean by "in the background". That can sometimes mean "the Activity exists but is in the stopped state". In that case, your data is fine. But your entire app process can be killed while the app is backgrounded and later restored when you return. In that case, your data will be gone if it is not properly saved to disk somewhere.
Is there a way to save large data without throwing this exception?
Yes, write it to a database and restore the data from there instead. Alternatively, you can use this library that automatically handles persisting / restoring your Bundles to / from disk for you : https://github.com/livefront/bridge .
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.
When I want to save some state behavior of the activity, The docs says that we should implement OnSaveInstanceState and OnReceiveInstanceState.
They say that this will save the activity state even after destroy or restarts. I care more about destroy (the activity is completely gone) , does that mean the bundles are considered persistent ?
when I open a pdf reader, clost it and open it again i see that it opens in the same page I was in. is this implemented using Bundles or oth
To store persistent application data use Shared Preferences. Shared Preferences are simply sets of data values that are stored persistently. By persistence, we are talking about data that persists across application lifecycle events. In other words, the application (or device, for that matter) can be started and stopped without losing the data. The next time the user launches the application, that data will still be available.
Some Games use Shared preference for exemple to store the level of the game reached, the player's name ...
see this link to learn how to use Android Preference API
Preference are simular to bundles However they are persistant and bundle are not!!
Keep in mind that if you need to store persistent data you have 4 options to do this:
Using Shared Preferences
Using SQLite Databases
Using Internal Storage
Using External Storage
Bundles are not persistent, the documentation says not to count on it, onSaveInstanceState() is called when the activity is about to be killed by the system, but for a known restart (for
instance on a screen rotation.) If your activity is killed because the system needs more resources (while the
activity is in the background), onSaveInstanceState() will not be called, but onPause() will. onSaveInstanceState()
really is not meant to save persistent data, as the doc states.
You can sorta consider SavedInstanceState() permanent but it's not recommended to use it for saving application related data in a persistent manner as It's not guaranteed to be called and it's not recommended by the authors themselves.
so, Only use them for saving user interface state changes (background color, currently selected items ,..) and use other method for persistence like : SharedPreferences, Files and SQLite.
Bundles are not persistent, and the documentation for them specifies that using them for persistence is not a good idea as their internal format may change between devices or even OS versions.
SharedPreferences, on the other hand, can be persisted and are the recommended way to store information like current app state.
Some relevant parts from SavingActivityState
:
Note: There's no guarantee that onSaveInstanceState() will be called before your activity is destroyed, because there are cases in which it won't be necessary to save the state (such as when the user leaves your activity using the Back button, because the user is explicitly closing the activity).
There is no guarantee data will be stored, especially in the case where your user exits the app.
Note: Because onSaveInstanceState() is not guaranteed to be called, you should use it only to record the transient state of the activity (the state of the UI)—you should never use it to store persistent data. Instead, you should use onPause() to store persistent data (such as data that should be saved to a database) when the user leaves the activity.
So like K-ballo said, used SharedPreferences if you have persistent data to store. onSavedInstanceState() is mostly useful for storing UI related data.
As every one else has recommended use shared preference and you should do this saving in onDestroy and onSavedInstance both.
When android is going to run low on memory, its just going to kill your application and call your onSavedInstance without calling onDestroy etc. Save your context in bundle it passes in onSavedInstance. When your app comes in foreground again, android will take care of restoring your back stack of activities. But this time it will pass you the bundle in your onCreate for each activity which will have all the values you saved in your onSavedInstance while your app was getting killed.
Hope this helps.
Short answer: It's not permanent.
Long answer: From "Android Programming - The Big Nerd Ranch Guide":
When onSaveInstanceState(...) is called, the data is saved to the
Bundle object. That Bundle object is then stuffed into your activity’s
activity record by the OS
...
So when does the activity record get snuffed? When the user presses
the Back button, your activity really gets destroyed, once and for
all. At that point, your activity record is discarded. Activity
records are also typically discarded on reboot and may also be
discarded if they are not used for a long time.