I know that Android provides a mechanism to allow developers to save and restore states in an Activity via the following methods:
protected void onSaveInstanceState (Bundle outState)
protected void onRestoreInstanceState (Bundle savedInstanceState)
I've seen numerous examples whereby people show how to save simple state (such as the current index in a listview), and many examples where people say that the listview should just be backed by a data model which will allow the list to be restored. However, when things get more complicated and the user interaction more involved, I've been struggling to find out the correct way to save this state.
I have a listview which contains custom controls for each row. When clicked a row will expand with animation to show additional details to the user... within the additional details view, the user is also able to change certain things. Now I know that if my Activity is destroyed (or my Fragment replaced), the current state reflecting the UI changes and the user's interaction with the list is now lost.
My question is how do I got about correctly saving this detailed state of my listview such that the state of each custom component row is also preserved?
Thanks!
Edit: Thanks for the replies below. I understand that SharedPreferences can be used to store key-value state, however, I guess I am more interested in the view states.... for instance Android will automatically save/restore the viewstate of an EditText field (i.e. its contents) in my activity , so how do I go about achieving the same thing with more complex custom components in a listview? Do I really have to save this state all manually myself (i.e. save exactly which listview rows have been expanded, save all the ui changes the user has made/toggled, etc.)?
I don't like to say this, but it seems that this must all be done yourself. The framework does not offer a pre-made method for "save state of view".
Whether you save the state in a Bundle or SharedPreferences, I think the point is that you need to determine state variables that reflect your current list choices, persist them yourself, and onResume() you need to reintroduce that state into your activity.
... quite a manual process, as you say ...
I am not sure but I assume SharedPreferences is what you might be looking for.
Make use of SharedPreferences in onPause() method.
where you can store all you activities data when it is moving to background.
When ever you want to restore the data fetch from the Shared Preferences.
Related
After poking through threads on the subject I still cannot figure out what to do. The thing is, I have a quite simple app, which has just one activity and one recycler view, where user can add some text. Now after I switch to another app or after I restart the app everything is lost. I know that I can preserve some necessary variables via onSaveInstanceState() method and Bundle, but is there a way to save absolutely everything? States of all view and all the data of the RecyclerView? Without writing down explicitly every singe variable?
Thank you!!
you can use shared preferences to preserve all types of variable and objects but you have to save all your variables after your activity is destroyed and read them when your activity is created
The guide from Android developers is rather ambiguous.
The default implementation of onSaveInstanceState() saves information about the state of the activity’s view hierarchy, such as the text in an EditText widget or the scroll position of a ListView widget.
These get stored as far as I know:
the Intent that started your activity (found out through testing)
properties of the objects in the Activity's view graph if they have been given an Id, presumably
Fragments that are not in the back stack and which have been set to retain their instance (Fragment#setRetainInstance(boolean))
However, in the API I have not found such contract being described and I am not sure whether this list is exhaustive either. I have not been able to find any documentation which clearly expresses what gets stored.
This is a possible duplicate to this question, however that question doesn't ask for sources or exhaustiveness and the answers there do not provide that. Are developers supposed to just look into the SDK in every single View, Activity, Fragment, AutofillManager etc. they use to see what gets saved and what doesn't? What are the guarantees?
Android Architecture Components provide the LiveData and ViewModel classes which are more lifecycle-friendly and designed for a leaner Activity/Fragment. These classes handle storing data across configuration changes, but I'm confused about their use compared to the Activity framework APIs. Are onSaveInstanceState(Bundle) and onRestoreInstanceState(Bundle) still necessary or useful for preserving activity state?
onSaveInstanceState & onRestoreInstanceState is still useful.
ViewModel holds data only when process is alive.
But, onSaveInstanceState & onRestoreInstanceState can hold data even if process is killed.
ViewModel is easy to use and useful for preserving large data when screen orientation changes.
onSaveInstanceState & onRestoreInstanceState can preserve data when process is in background.(in background, app process can be killed by system at anytime.)
Assume a scenario :
user is in activity A , then navigates to activity B
but because of low memory Android OS destroys activity A , therefor the ViewModel connected to it also destroys. (You can emulate it by checking Don't keep activities in Developer options)
now user navigates back to activity A, Android OS try's to create new Acivity and ViewModel objects. therefor you loosed data in ViewModel.
But still values in savedInstanceState are there.
As well as the other answers which talk about the ViewModel's persistence beyond simply configuration changes, I think there are a couple more use cases:
Performance reasons
Sometimes you don't want to store all of the latest values of view attributes in the ViewModel for performance reasons. You may have greater need to save them when the view is being re-created. For example, user's scroll position on a view within your activity/fragment. You probably don't want to save the scroll position every time the user scrolls. But you might want to save that onSaveInstanceState so you can restore that when the view is recreated (onRestoreInstanceState).
Initialization to perform after restore
Some views may require initialization especially because of the restore, due to the complex design of those views not being able to save everything. For example, I had a WebView and if the user was in the middle of loading a page during a configuration change, I want the WebView to try to load the new page (rather than the old one). After restoring the state, the observers of LiveData will get the latest values but this doesn't help much with something like this (I only want the view to load a page from the ViewModel at the point of restore, not at other times). So we just do that initialization via the restore state.
Final word
With all this stuff I would advocate keeping your onSaveInstanceState and onRestoreInstanceState as simple as possible. Ideally just call a method on the ViewModel and that's it. Then we can extract all of the logic from the view into the ViewModel, and the view is just left with boilerplate code.
This is a practice I've been using for a while now, but it seem to be deprecated, the literature seems to say that the correct way to save and restore data when the screen goes to the background or rotates is fragments.
I would like your opinion on this
What I've been doing in my apps is create a class I call ApplicationDataHolder()
This has all the variables that define the state of each activity and fragment stored in it.
For example I have an activity that shows a list of tickets and two widgets one for the way to sort the tickets and one to select if it will be ascending or descending.
For this I have created the variables List _tickets, SortOrder _order and boolean _ascending in my DataHolder() and given them default values
Whenever the activity is recreated/created for the first time, I access those variables to set default values (what the default sort order will be, what the initial list will be)
Is this not the optimal way? could this cause problems (for example after the screen has rotated too many times) what is the benefit of using fragments or saveinstancestate/restoreinstancestate over this?
Thanks in advance for any help you can provide
the correct way to save and restore data when the screen goes to the
background or rotates is fragments
Here they are talking about data that is obtained dynamically, either as input data from the user or data coming from a sensor or web-service. This data needs to be restored using onSaveInstanceState() and onConfigurationChanged() when a state change occurs, such as rotation or a tab swipe.
Initial values can of course be saved in a central global constants file, nothing wrong with that.
Before I begin, I have read this, this and some more articles online. I am still unable to find a right way to do it.
So I have just one activity in my App, and 6 fragments. First one is a ListFragment which loads a list from a SQLite table. When user taps on a row in this list, I do 2 things:
1) Get an int from that row through a listener, and pass it back to the parent activity which stores it as a class variable using a simple setter method.
2) Replace this ListFragment with another simple Fragment. This new Fragment uses a simple getter() on that class variable to retrieve some information from a different table, and show all the details to the user.
So far so good. Now if I am on this details Fragment, and I change the screen orientation, the activity state is not reloaded (as I am checking if savedInstanceState is null in the onCreate()), but however, the class variables lose their value, and my app crashes.
Basically I am trying to pass data from the ListFragment to the details Fragment. I am doing it through the activity, which is causing a problem. As per Android Documentation:
All Fragment-to-Fragment communication is done through the associated
Activity. Two Fragments should never communicate directly.
There is no specific code which is giving me trouble, so didn't post any.
The onSaveInstanceState and onRestoreInstanceState is only used to save and restore per-instance state of an activity in case your activity is destroyed by OS (for example, to free the memory or in order to recreate it when the device orientation was changed). So you can save your variable in onSaveInstanceState and get them back using onRestoreInstanceState.
For your next question, I think this article will help you. Also answer by Gene for this question will help you.