Usually fragment states are saved using onSaveInstanceState() and restored with onViewStateRestored() or by using a state instance fragment. However I noticed I can save fragment variable data by declaring the variables static. Obviously this won't work with view components but for primitives it seems to work fine. I wanted to know whether this static data is guaranteed to be preserved without saving it in onSaveInstanceState().
No it is not guaranteed.
When for example Android decides to kill your process, the onSaveInstanceState() is called and the bundle will be available when you get back to your app as savedInstanceState, however all of the static variables will lose their values when your app gets killed. This applies to all of the static variables.
Related
In my code, I use two methods to pass data to a new fragment. Either I pass data through Bundle or sometimes write setters to pass data.
Both works fine, no issues faced yet.
But now, I am optimising my code keeping in mind savedInstances, orientation changes or any other possible way where data can be lost.
So, the exact doubt in my mind is whether the data sent via bundle remains intact by default on orientation change / fragment restored from background. Or we have to use savedInstance in the case of bundles as well. As per my knowledge, data set through setters get lost.
Whenever the OS needs to re-layout your view, it will call onCreate and onCreateView with a saved instance state. If you are using a constructor and passing variables, you will lose whatever you set. If you are using a bundle and are using it to directly change some of your variables, you will likely overwrite them with the original values in your bundle. To get around this, just check if the bundle is null before performing the mutation.
TLDR: Passed in bundle will remain intact through orientation changes and instance restores. You can add extra data to the saved instance state bundle in onSaveInstanceState.
As you said, OS can recreates your fragment (across a configuration change or when OS need to reclaims memory), data will be lost. And you consider between using saveInstanceState and passing Bundle as fragment's argument.
Using bundle as fragment's argument is less complicated, easier to maintain. However, argument must be set before fragment is attached to activity, that means you can not change argument later. So, if your passing data is fix, argument bundle is the best choice
If your passing data can be changed while running, setter + saveInstanceState is the only way to go
I'm trying to understand configuration changes and instance state a bit better.
Right now I know that an action such as rotating the screen is considered a configuration change, which destroys and recreates the activity. Sometimes this means that if you aren't careful you may lose values (for example if you had a list of numbers that gets reset to all 0's when you rotate the screen).
There are usually methods you can use such as onSaveInstanceState, or onRestoreInstanceState, or checking if savedInstanceState is null or not in the onCreate method, etc -- for saving and restoring values so you don't lose anything when something changes.
My questions:
Are there multiple types of configuration changes that need to be taken into account? For example if I get my app to work as expected even with screen rotations, does this also imply that it will carry over and act as expected with other forms of configuration changes?
How do I know which variables I should be saving into my instance state variable and which will be auto-preserved when a configuration change takes place? Is it good practice to store all your member variables in the instance state in onSaveInstanceState and then re-apply them in onRestoreInstanceState?
For example if I get my app to work as expected even with screen rotations, does this also imply that it will carry over and act as expected with other forms of configuration changes?
Generally, yes. I would phrase it more as: a well-implemented approach to configuration changes will handle all possible configuration changes.
So, for example, if the user runs your app, presses HOME, goes into Settings, changes their locale, then returns to your app through the overview screen (a.k.a., recent-tasks list), your top-most activity will undergo a configuration change. The goal here is for you to load in fresh string resources and stuff, to reflect the newly-chosen language. But, if you save some user-facing string in the saved instance state Bundle and simply use that string in the new activity — instead of calling getString() again — then you will have a value from the old language.
Many developers only think of orientation changes. Usually, if you handle an orientation change, all other configuration changes are handled "for free". But, that's not always the case, which is why you need to think it through.
How do I know which variables I should be saving into my instance state variable and which will be auto-preserved when a configuration change takes place?
The primary things that are "auto-preserved" are:
the Intent that was used to start the activity
user-mutable state in widgets in your UI (e.g., text in an EditText)... assuming that either you do not override onSaveInstanceState() or chain to the superclass implementation
If you are using fragments, your fragments will either be retained (i.e., same instances used) or re-created (i.e., fresh instances of the same class used) on a configuration change.
What is not handled automatically are any fields/data members of your activity. Those you need to decide:
Is this something that I need to hold onto across a configuration change, or is it merely a cache for something to be rebuilt in the new activity?
Is this something that I can reasonably put in the saved instance state Bundle, or is it of a size (e.g., Bitmap) or data type (e.g., Socket) that cannot be held onto that way? For these, you will need to pursue other techniques besides the saved instance state Bundle (retained fragment, persisting the data, etc.).
onSaveInstanceState and onRestoreInstanceState could be called when the Android OS is running low on memory and it needs to kill some processes. For example, if you app enters the background and is later resumed, these methods could be called.
All Views are automatically saved by the Activity. For instance, an EditText's text and a checkbox's checked state will be saved and restored automatically after a configuration change. All other data (including instance variables) for the Activity should be saved by you manually using the onSaveInstanceState and onRestoreInstanceState methods.
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.
For restoring the state of the activity after it is recreated (for instance after the screen-orientation change) I implemented onSaveInstanceState() and onRestoreInstanceState(). It is simple to save/restore simple information like int, double etc. But what about saving/restoring objects like Timer?
You cannot store "live" objects (like db connections) in the Activity arguments or saved instance data. Those mechanisms are designed so that the application can be completely stopped, so it only works with things that can be "serialized" and later restored.
What you can do is use fragments. If you add a fragment without UI (check here, look for
“Adding a fragment without a UI”) and call on it setRetainInstance(true) the fragment will get reattached to the activity, surviving any configuration change.
Hope it helps. (Remember you can use fragments with old Android versions by using the support package)
When an app have been put into the background and later resumes to an activity, is it possible that static class variables set in another Activity could have been reset by the garbage collector and have got the value set to zero?
I use a couple of public static int variables in my main Activity and use them as global variables in various other Activities. I have received a crash report in the developer console from Android Market where the only explanation I can find is that the app resumes to an Activity which uses the value of a public static int variable in another class, but the value has (mysteriously?) become zero. I know that it was set to something else when the app first started. Is this at all possible?
If my suspicion is correct, what is the recommended way to preserve the values of the global variables when an app is put in to background? Save them in SharedPreferences in OnPause() or use onSaveInstanceState or something else?
When an app have been put into the background and later resumes to an activity, is it possible that static class variables set in another Activity could have been reset by the garbage collector and have got the value set to zero?
It would not be "reset by the garbage collector". However, the process may have been terminated by Android, to free up memory for other applications.
what is the recommended way to preserve the values of the global variables when an app is put in to background?
Static data members should only be a cache. Data you want to retain regardless of what happens (e.g., process being terminated) needs to go in some persistent store, such as a flat file, SharedPreferences, or database. onPause() is a likely time to arrange to persist that data, as any time after that, your process could be terminated without notice.
Be careful with static variables. Follow the link for an explanation concerning Singleton (which also use a static variable to maintain state): https://stackoverflow.com/a/9004638/1127492
You can save this values on SQLite on method onDestroy in activity or another method with response to exit.