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.
Related
Problem: Sometimes / on some devices the activity calling startActivityForResult (activity A) to launch activity B is being destroyed after calling startActivityForResult & before entering onActivityResult. We get a newly created instance of activity A to return to in onActivityResult - this causes our ViewModel (along with all other member variables) to be lost.
The standard thing to do would then be to restore the ui state using SavedInstanceState. This can't be done in this case due to the size of the object we need to restore - attempting this results in a TransactionTooLargeException. The ViewModel is too large for a Serializable or Parcelable.
Question: Is it possible to force our Activity to be kept intact during this workflow? Or is there another design that would let us avoid this problem? Saving any of the ViewModel's data to disk is not an option.
Context: This is a project where we store a list of images (as byte arrays) taken from the camera one at a time, and some related info about those images in a ViewModel. These are staged in a RecyclerView, where they can be uploaded when the user is done adding images. We add items to this ViewModel by calling startActivityForResult to launch a camera activity and return the resulting image.
We may only be seeing the problem of activity A getting destroyed due to the "Do not keep activities" setting in Developer Options being turned on, and this may not accurately represent how Android would reclaim resources (e.g. the conversation at the bottom of this thread - https://stackoverflow.com/questions/21227623/whats-the-main-advantage-and-disadvantage-of-do-not-keep-activities-in-android#:~:text=Android%20OS%20has%20this%20property,replicate%20the%20same%20scenario%20easily). Still, ideally we want everything to work with this setting on. Right now if activity A is destroyed, we lose our member variables and the ViewModel that we were in the process of building, and don't have a way to recover it.
Storing the ViewModel's data in a fragment (as discussed here: Fragment, save large list of data on onSaveInstanceState (how to prevent TransactionTooLargeException)) won't work since our activity is being destroyed, causing any associated fragments are as well. We actually have a fragment we're using in this way, which loads & holds a list of objects from the server to be selected from and associated with each image - this fragment ends up getting recreated along with the activity when its destroyed and then performs this load again.
No, what you want is not possible. If you launch another Activity using startActivityForResult() and that Activity requires resources, the launching Activity will be killed. There is nothing you can do to prevent this. It is standard Android behaviour and will happen, especially on low-end devices.
If your ViewModel is too large to save as the instance state, you will need to put the data somewhere else: SQLite database or a local file. Then store the name of the file or some key to the database as part of the saved instance state, and when the Activity is relaunched, restore the data from the file or data base.
Note: you shouldn't keep that much data in memory anyway, as you are wasting valuable resources. Only keep the data you really need in memory.
I have an object which I know is persist, because it performs it's behavior. It is an extension of RecyclerView.Adapter with SelectionTracker inside, which I am connecting to my RecyclerView only once. After I rotate my screen, Activity recreates itself and all member fields turn to null, including field with extension of RecyclerView.Adapter. But it is still somewhere there, because RecyclerView still works and I can select and deselect items.
How can I reacquire a reference to this object?
I don't want to serialize or parcealize it, because it is in memory and working.
Also I wonder, under which circumstances Android will dispose objects in such a situation?
You can solve this in 2 ways:
Tell Android that your app will handle the configuration change itself by adding android:configChanges="..." to your manifest. In this case, when the screen orientation changes, Android will not kill and recreate your Activity, it will simply call onConfigurationChange() on the existing instance and you can manage it yourself.
Use onRetainNonConfigurationInstance() and getLastNonConfigurationInstance(). Upon an orientation change, Android will call onRetainNonConfigurationInstance() just before it kills the current Activity. In this case, implement the method and return this (the instance of the current Activity) from the method. Android will then kill the Activity and create a new instance. In onCreate(), you should call getLastNonConfigurationInstance(). If this returns a non-null value, then this will be a reference to the Activity that got killed. You can now copy any data from member variables in the old instance to member variables in the new instance. In this way, you can take "migrate" anything necessary from the old instance of the Activity to the new instance.
I have a form activity every Edittext open another activity when I change data and I get it in my activity I lose the others that I have changed them before . this is initial state and this is what I get by changing any data.
You need a consistent data model in which to store those values. If it's size isn't larger than 2MB you can make this model Parcelable and seriali. After that you must cache those values either in savedInstanceState, SharedPrefs, singleton (I do not recommend it), or local DB (i.e. sqlite). After doing so, whenever your activity is displayed you should check if you already have a saved value for that field and fill it with that.
you can fix this by letting it open another fragment instead of activity and make sure that you dont destroy the activity.
so overall view you gonna have 1 main activity and each edit text will replace fragment view
Bonjour Flora
An activity is not supposed to be persisted if not displayed. That means it could stay as you left in when returning from another activity but it also may not.
If the system needs to free memory it will destroy the activity and recreate it when the user gets back to it. This is the expected behaviour on Android.
So what you should do is store your data when the activity goes out (in onPause() method) and fill your edittexts when the activity goes back in (in onResume() method)
Pay also attention that you need to handle what they call configuration change (such as screen rotation) using onConfigurationChanged() that allows you to pass some information between the former configuration and the latter for reuse.
Finally you should build your layout according to Android's guidelines (material design) for your UI to look a bit more conventional ;)
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.
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.