Why is Bundle not suitable for large data, and ViewModel is? - android

I read this, and it says:
For simple data, the activity can use the onSaveInstanceState() method
and restore its data from the bundle in onCreate(), but this approach
is only suitable for small amounts of data that can be serialized then
deserialized, not for potentially large amounts of data like a list of
users or bitmaps.
My question is why? How does the ViewModel differs from Bundle with regards to making the data persistent between instances?

Data stored in Bundle is serializable and could survive process die - you can restore it after application is launched again. On the other side, ViewModel only survive during configuration change (for example, rotation of the screen) and didn't save it's data if component die.

Bundle is class used to pass data between components of android like activities, fragments etc.
Mainly for activity, when activity is under any configuration changes, bundle helps to save some small amount of user data to be restored on new configuration applied. Due to limited memory provided for any App, it's stated that,
this approach is only suitable for small amounts of data that can be serialized then deserialized, not for potentially large amounts of
data like a list of users or bitmaps.
Also, it is not anything related to activity lifecycle.
From ViewModel guide, it's stated that
The ViewModel class is designed to store and manage UI-related
data in a lifecycle conscious way. The ViewModel class allows
data to survive configuration changes such as screen rotations.
So, any data which you need to survive any configuration changes can be part of ViewModel class, it can be any large amount of data.
Let me know if still confused, hope it helps !
Edit :
Thin line difference is
Bundle is something which is provided by activity once it's under configuration change. So, the OS parcels the underlying Bundle of the intent. Then, the OS creates the new activity (yes, it's new object of same activity), un-parcels the data, and passes the intent to the new activity.
ViewModel objects are something that are provided by ViewModelFactory which is out of context for activity & it doesn't rely on activity instance.

Related

Is it wrong to put data in extra while in fragment?

I have one Activity and want to share data between that Activity and fragments. I put data in extra while in a fragment and put also other data in it. That way I Have a shared Bundle across my application. I only see examples of passing a Bundle to an Intent but its also possible to change that data while in another fragment. This does not break with the self-containment of fragments. I dont put them in some method in activity because then you will have to cast the activity. Can anybody tell me its right to do? I know about shared pref but I dont want a file based solution. I know about passing parameters with newInStance but I also need to save data back in fragments. passing parameters is only forward not shared.
Passing data from activity/fragment back & forth using Bundles would have some limitations and issues for instance:
To pass a complex object, you'd need to use a serializable marked with a key.
Keeping shared keys in different parts can lead to runtime
errors or data loss if keys are wrong.
Serialized objects are not recommended, but it can be solved with Parcelables. But maintaining that is not that easy for complex objects check here, and you would need to customize that for different types of objects.
Still you don't share data among different fragments but they're just transported; and need to be transported over and over again when you go to a new part of your app.
Not guaranteed to keep the data if the activity is recreated.
Instead of that you'd use ViewModels through MVVM structure where:
Data can be shared on different levels of lifecycle scopes; this means that if the ViewModel is instantiated through ViewModelProvider in activity; then it can be accessed in any part of the activity, or underlying fragments. And if you want to keep only data shared between any fragment and its underlying fragments; you'd bound the ViewModel instantiation to that fragment instead.
ViewModel is instantiated once in the owner, and accessed in the subordinate fragments with no re-instantiation.
If the activity is re-created, it receives the same ViewModel instance that was created by the owner, and the view's data won't be lost.
When the owner activity/fragment is finished, the framework automatically calls the ViewModel's onCleared() method so that it can clean up the resources.
Here is a code lab that you'd check.

Saving activity's state with onSaveInstanceState() and ViewModel

I was left with some questions regarding ViewModels after reading this:
https://developer.android.com/topic/libraries/architecture/saving-states
It says here that you should use a combination of both a ViewModel for configuration changes (like screen rotation), and using onSaveInstanceState() for all other cases where an activity is destroyed and then recreated in order to save the UI state.
My question is how do we know the way to restore the state when onCreate(Bundle) is called - should I use the ViewModel or should I use the bundle received as a parameter? When the configuration changes, onSaveInstanceState() is also called, and obviously onCreate() is always called.
If I only restore the state from a ViewModel, it won't always remain with the correct data (since the activity could have been destroyed due to other reasons than configuration changes). If I only use the bundle I save in onSaveInstanceState() then why would I use a ViewModel to begin with?
I think it's good to think of this sources as a chain.
You have 2 sources of data - ViewModel, that is faster but lives less and saved instance state that is slower but lives longer.
The rule is simple - try using your ViewModel and if it is not populated use the bundle from onSaveInstanceState().
When you do val model = ViewModelProviders.of(this).get(MyViewModel::class.java) in onCreate() you can check if you get a new instance of viewModel. Then, if it is a new instance (i.e. it's data fields are empty) you can get some basic data from your bundle, like content id, and fetch data from the backend or database based on that id, populate your new ViewModel with it and then populate your activity from the ViewModel (if you are using LiveData it will be very natural).
Next time onCreate is called you repeat the process, either populating your activity from ViewModel or populating your ViewModel using data in the Bundle and then populating your activity from your ViewModel.
Update:
Actually there is very similar approach described in the official docs. The only difference is that you pass the bundle to ViewModel and it decides if it needs fetching data, I was not specific about this mechanism.

What's the best way to preserve a model across android configuration changes?

In my application, I have a fragment which renders a model object fetched from the server. The model class implements Parcelable so when the device is rotated, I save the model in onSaveInstanceState() and then I load the model in onCreate(). If the savedInstanceState bundle does not contain the model in onCreate(), I make the network request again.
My question is: Is it better to preserve the state of the model by having it implement Parcelable and saving it in onSaveInstanceState() OR is it better to simply set the Fragment's setRetainInstanceState(true).
In my opinion setInstanceState should be used only in special cases, while onSaveInstanceState() should be used as standard approach to keep fragment's state. This is common way for keeping state of all Android's components.
Keeping fragment in memory can lead to situations like strange views behavior and memory leaks.
The API Guide recommends using a retained headless data fragment.

What's the point of bundles in Android?

If I pass a bundle to an activity from MyActivity to MyFragment using setArgs and getArgs. Couldn't I have just passed the data objects through the MyFragment constructor? What's the point of all this bundle stuff? Is it faster?
Side question: When I pass bundles around, are they aliased? So that editing the bundle will change everything?
The bundle is needed to manage the lifecycle of the fragment and to allow the OS to create again the object. Other info here docs
Well, I think this needs some introducing:
If an Activity or Fragment will be f.e. destroyed the state of them will be saved. Therefor this state must be saved by the system to be able to recreate it. The best thing to do is to save it as a byte code with all information necessary to rebuild the objects which define this state. With the use of byte code it doesn't matter of which type this objects are. The transformation of objects into byte code is called serialization.
Android has it's own serialization mechanism called Parcelable. It's much faster than the serialization Java provides by default and because of this it should be the prefered way to use in Android.
Now Bundles only take Objects, which implements the Parcelable or the Serializable interface, and primitives. They are used to save the different objects which define the state of Activities/Fragments at one place.
Because the arguments which you can pass to a Fragment will also be serialized by the system for future use, a Bundle is needed. This is the reason why a Fragment should only use a non-arg constructor and pass the parameters through the arguments.
In short: A Bundle is used by the system to save and recreate the state of Activities or Fragments. Therefor the system uses a format for this data which is easily readable, a byte code.
the big deal in budnles is that they work between applications. This makes for the possibility to run, for example, the browser, with a website that is programmed in the app. If you would start the browser whilst passing data through a constructor, the browser window would run in the same thread as the application that you started running it through. Bundles and intents make the operating system more secure.

Android Fragments Retaining Data

How does one best retain data within a Fragment as its activity goes through an onCreate/Destroy cycle like from Rotation?
In our setup we have potentially large lists loaded from our servers into the fragments custom list adapter and we want to smooth out the UX by not making them reload on rotation. The problem we had with setting the fragment retainInstance=true; is that our adapter has a reference to the context of the original activity and would therefore leak memory. Could we just store the data in the fragment and re-create the adapter; amd if so is that really proper practice?
The next idea is to store the data into a session singleton object and retrieve after rotation which presents a few problems with stale data but we can easily overcome.
The other alternative I see, that seems like it is the *best solution, is to save the data into a bundle and restore into the new fragment after rotation; However, we have quite a few objects that would need to be stored throughout the app and some of our objects are complex, contain lists, multiple types, and would be a pain to make parcelable. Is there a better solution or do we have to bite the bullet and make them Parcelable?
Just prevent the Activity from recreating itself on rotation (etc). Add
android:configChanges="keyboardHidden|orientation|screenSize"
to your Activity definition in your AndroidManifest.xml. Then there's no need for saving anything on rotation.
EDIT:
If you don't like that solution then you've got no choice but to use the onSaveInstanceState mechanism. If you've got complex data, just make your classes Serializable and add them to the Bundle that way.
Setting the
android:configChanges
attribute in Android manifest is the hackiest and most widely abused workaround for disable the default destroy-and-recreate behavior.
See more about that at
Handling Configuration Changes with Fragments :
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
According to http://developer.android.com/guide/topics/resources/runtime-changes.html,you completely can save the data in the fragment as long as it isn't associated with the activity, views, etc. Bundles are really not meant for heavy amounts of data and serializing is slow, so a fragment is ideal for large amounts of data.
It might not be possible for you to completely restore your activity state with the Bundle that the system saves for you with the onSaveInstanceState() callback—it is not designed to carry large objects (such as bitmaps) and the data within it must be serialized then deserialized, which can consume a lot of memory and make the configuration change slow. In such a situation, you can alleviate the burden of reinitializing your activity by retaining a Fragment when your activity is restarted due to a configuration change. This fragment can contain references to stateful objects that you want to retain.

Categories

Resources