I programmatically create a RelativeLayout with some other views inside it and add it to the parent which is defined in XML. But all the views (incl. the layout) that were created programmatically disappear after the activity is re-created. Do I need a SharedPreferences object to save the values and then re-create the layout or is there an easier way to save it?
P.S. all new created views get an id assigned
Do I need a SharedPreferences object to save the values and then re-create the layout
You say that you are creating these widgets in onResume(). Your code in onResume() needs to know what widgets need to be created. If this is purely transient information, and you are worried about things like the activity being destroyed and re-created due to a screen rotation or other configuration change, use onSaveInstanceState() on the activity or fragment to pass data about these widgets from the old to the new instance of the activity. If, on the other hand, you are worried about being able to re-create these views several months later, you need to store this information persistently: SharedPreferences, database, some other file structure, "the cloud", etc.
I recommend you look into Activity.onSaveInstanceState(Bundle):
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()). 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 [ via super.onSaveInstanceState() and super.onRestoreInstanceState()], otherwise be prepared to save all of the state of each view yourself.
https://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)
as well as my answer here:
How can I assign an ID to a view programmatically?
Related
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.
I would like to know if there is a method to Save a Custom Class while rotating in an Android app.
I want to save all instanced object called of the MatrixGame class...someone can help me?
Thanks
Since the Fragment lifecycle is independent - when you're using a Fragment you can set it so it doesn't get destroyed upon configuration changes.
As you noticed, the Activity class gets destroyed and re-created when you rotate the app (or apply other configuration changes), if you want to persist the Activity state you can use sqlite and save whatever you need in the onPause() method. Then in the onCreate() method check the DB for last known state.
If you want to avoid saving the state "forever" (meaning, the user turns off the app and tomorrow when she turns it back on - you want to start fresh and not load the last known state) you can add a timestamp and set a threshold which, if passed, the data is considered stale and gets disregarded.
Two comments:
As #saprvade wrote, Fragments gets destroyed by default, in order to prevent it you should call: setRetainInstance(true)
In case you want to implement #saprvade's other suggestion (have the activity ignore rotation changes) you can see the following explanation of how to implement: https://stackoverflow.com/a/456918/1057429
You should serialize your object. You can implement Parcelable or implement Serializable (Parcelable is several times faster). Then you will be able to put it in a Bundle in onSaveInstanceState and restore it in onCreate or onRestoreInstanceState.
Also, you can serialize your object to String, e.g. json string.
Another option would be to store your object in a database or a file. It depends on your needs.
If you don't want to recreate this object on screen rotation you can change the lifecycle of your Activity by adding a configuration change flag in AndroidManifest.xml. If we are talking about a Fragment, you can call setRetainInstance(true) to avoid fragment recreation on rotation.
I want my app to support different layouts for right-handed and left-handed users. So after changing the respective preference I want to restart the activity in the same way as it restarts when e.g. orientation changes.
What I tried so far:
1.
Intent intent = getIntent();
finish();
startActivity(intent);
This does not store and load the saved instance state
2.
View cv = findViewById(android.R.id.content);
SparseArray<Parcelable> state = new SparseArray<Parcelable>();
cv.saveHierarchyState(state);
setContentView(desiredCv);
cv = findViewById(android.R.id.content);
cv.restoreHierarchyState(state);
Even then many things aren't as they should be.
I think that in the end I could figure out how to change layout properly without restarting but it would be much easier to do it in the same way as for system-defined configuration changes.
You could use Fragments and do it programmatically. Following the same way you could also rearrange dinamically your elements in the UI but I think it would be complicated to maintain.
OnSaveInstanceState() is not called on an Activity being finish-ed. And I'm not aware of a way to let Android handle this for you.
The solution may be to create 2 different layout files. Then you programmatically select the right one in the onCreate() method based on the preference value that has been updated.
If your two layouts use the same ids for those views that you need to restore, then you can implement two methods that save these states in a Bundle and retrieve these states from the Bundle. When you want to change layout, start a new activity with the proper intent (telling the activity which layout to load); in the bundle associated with this intent save what you need and retrieve it in onCreate() to update the views. You don't need to duplicate the code in the case you use layout dx or sx; you can use the same code since the two layouts use the same ids.
In this way you still have your one activity and can reuse your code. And the same code you use to restore the state between layout changes can still be used with onSaveInstanceState() and onRestoreInstanceState(), since, again, the ids are the same.
To make it short, use bundles.
If you are using API level 11 or higher (Android 3.0 or later), you can call recreate() in your activity. This will destroy the current instance and create a new one and should do the same thing as what happens during a configuration change (ie onSaveInstanceState() will be called, etc.).
The docs concerning Activity recreation state:
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.
The docs for View.setTag(Object) state:
Sets the tag associated with this view. A tag can be used to mark a
view in its hierarchy and does not have to be unique within the
hierarchy. Tags can also be used to store data within a view without
resorting to another data structure.
My question is: if I set a tag on a View (that has a unique id) in my activity's layout and then the activity is destroyed by the system and subsequently re-created, will that view, when recreated, automatically be tagged with the Object I originally set?
if I set a tag on a View (that has a unique id) in my activity's layout and then the activity is destroyed by the system and subsequently re-created, will that view, when recreated, automatically be tagged with the Object I originally set?
No, based on my reading of the source code.
The system expects to recreate the activity at some later date, so I could see it keeping around a reference to the original tag.
That is not always possible, as the saved instance state Bundle needs to be transportable across process boundaries.
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.