Does ViewModel survive Activity save and restore? - android

Instances of the new ViewModel class can survive configuration changes if used in the following fashion:
mViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
However, in addition to configuration changes, there is also a save-restore scenario when the entire application's process is being killed.
Will fields' values inside ViewModel be preserved during save-restore scenario?
Edit: based on the answer to this question, I wrote this article: Android ViewModel Architecture Component is Dangerous

According to ViewModelProvider documentation (check get method), ViewModel is not preserved when app's process is killed:
The created ViewModel is associated with the given scope and will be retained as long as the scope is alive (e.g. if it is an activity, until it is finished or process is killed)
In addition check Ian Lake answer to similar question:
https://medium.com/#ianhlake/you-are-correct-the-viewmodel-is-destroyed-if-your-process-is-killed-by-android-ef611fcd51e6
You are correct: the ViewModel is destroyed if your process is killed by Android. Just like before, you should use onSaveInstanceState() to store any data you must have to later recreate your Activity in the same state as before.
I also recommend reading
Android ViewModel Architecture Component is Dangerous.

It seems Google offers a solution for this now
Saved State module for ViewModel
UI State is usually stored or referenced in ViewModel objects, not
activities; so using onSaveInstanceState() requires some boilerplate
that this module can handle for you.
When the module is set up, ViewModel objects receive a
SavedStateHandle object via its constructor. This is a key-value map
that will let you write and retrieve objects to and from the saved
state. These values will persist after the process is killed by the
system and remain available via the same object.
Setup
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-rc02' (November 7, 2019)
Usage
In order to set up a ViewModel to receive a SavedStateHandle you need
to create them using a Factory that extends
AbstractSavedStateVMFactory.
SavedStateViewModel vm = new ViewModelProvider(this, new SavedStateVMFactory(this))
.get(SavedStateViewModel.class);
After that your ViewModel can have a constructor that receives a
SavedStateHandle:
public class SavedStateViewModel extends ViewModel {
private SavedStateHandle mState;
public SavedStateViewModel(SavedStateHandle savedStateHandle) {
mState = savedStateHandle;
}
...
}
Storing and retrieving values
The SavedStateHandle class has the methods you expect for a key-value map:
get(String key)
contains(String key)
remove(String key)
set(String key, T value)
keys()

Related

How does the Android Viewmodel works internally, internal working of view model

How does the Android Viewmodel works internally?
How Viewmodel save the data when the activity gets destroyed and recreated when the device get rotated
View Model's Internal Wokring:
View Model:
View Model is a lifecycle awared class, designed to store and manage UI related data. It a main component in MVVM architecture.
When a view model is created, it is stored inside activity or fragment manager.
Benefits:
Lifecycle awared
Hold and share UI data
Survives in rotation and retains data
Here we can raise a question that how we can get same instance of view model when new instance of activity is created while rotating screen from portrait to landscape ?
Answer:
To create a viewmodel object, ViewModelProvider class is required.
ViewModelProvider is the utility class which is used to create the instance of viewmodel in the following way.
Create a ViewModelProvider instance
Get the desired viewmodel from the viewmodel provider object
Internally creation of ViewModelProvider required two parameters.
ViewModelStoreOwner: it is an interface.It has just one method which returns the
ViewModelStore.
Factory: It is a nested interface in the ViewModelProvider class and is used to manufacture viewmodel objects.
val viewModelProvider = ViewModelProvider(this)
val viewModelProvider2 = ViewModelProvider(this,viewModelFactory)
If the factory is not passed then the default factory is created. Custom factory can be created for the parameterized viewmodel.
So now we have instance of viewmodel provider,
Now let’s get our viewmodel object
val viewModelProvider = ViewModelProvider(this)
val viewModel = viewModelProvider.get(LoginViewModel::class.java)
As we can see, we simply just called the get(arg) method with the desired viewmodel class reference and our viewmodel object was created.
So all the magic happens in this get method
This method gets the canonical name of the viewmodel class,creates a key by appending DEFAULT_KEY then it calls the another get function which takes the key and viewmodel class reference
This method checks the viewmodel instance in the viewmodel store first.If the viewmodel instance is there in the viewmodelstore then it simply returns that instance .If viewmodel instance is not there then it uses the factory to create the new instance and saves that instance in viewmodel store and then it return the viewmodel instance.
This viewmodel store is linked to ViewModelStoreOwner so our activity/fragment has their own viewmodel store.
It is ViewModelStore which stores the viewmodel and is retained when the rotation occurs and which returns the same viewmodel instance in the new activity instance.
Interview Question : Viewmodel store is linked to activity / fragment and when in the process of rotation current instance is destroyed and new instance is created then how this ViewModelStore object is still the same?
Let’s know about this magic
ViewModelStoreOwner is an interface. ComponentActivity implements this interface.
In above implementation , we can see that in the new activity object
when viewmodel store is null then it first checks with the
NonConfigurationInstance which returns the previous activity’s
viewmodel store.
If activity is being created for the first time then always new
viewmodel store objects will be created.
So It is NonConfigurationInstance object which is passed from old destroyed activity to newly created activity when rotations happens.It contains all the non-configuration related information including viewmodel store which contains the viewmodel of old activity object.
Answer is inspired by This Link
How does the Android Viewmodel works internally?
Android's ViewModel is designed to store and manage UI-related data in such a way that it can survive configuration changes such as screen rotations.
ViewModel gets called by an activity that previously called it, it re-uses the instance of that ViewModel object. However, if the Activity gets destroyed or finished, counterpart ViewModel calls the onClear() method for clearing up resources. Meaning if you have added something like this to your ViewModel:
override fun onClear() {
super.onClear()
clearAllLiveDataValues()
disposeAllVariables()
}
Function calls added here will be invoked.
How Viewmodel save the data when the activity gets destroyed and recreated when the device get rotated
ViewModel has its own lifecycle that allows itself to recover its state, and the transient data it holds, during screen rotations.
NOTE: Activity and ViewModel's lifecycle are both ephemeral. Allowing the ViewModel to handle critical or sensitive data during configuration changes IS NOT RECOMMENDED.
Your application should use either shared prefs, secured storage (if necessary), local database or cloud storage when you are expected to handle critical or sensistive data in a specific screen or part of your app.
I recommend that you read the following:
https://developer.android.com/topic/libraries/architecture/viewmodel
https://android.jlelse.eu/android-architecture-components-a563027632ce
https://medium.com/androiddevelopers/viewmodels-persistence-onsaveinstancestate-restoring-ui-state-and-loaders-fc7cc4a6c090

Is there a data loss while switching to darkmode

When the Dark mode is enabled using the AppCompatDelegate.MODE_NIGHT_YES in kotlin, the activity or the fragment is recreated. If we are calling an API in the same class it will be recalled. Is there a way to eliminate recalling the API.
The ideal way to handle this is by using ViewModel.
As the official documentation says:
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.
What it means, is that your ViewModel will survive your UI recreation and there will be no data loss or API double calls.
Create your ViewModel class.
class MyViewModel : ViewModel() {
fun callAPI() {
// call you're API here
}
}
Just obtain your ViewModel once in Fragment's onCreate() lifecycle callback.
val model = ViewModelProviders.of(this)[MyViewModel::class.java]
As you want that your API call will be done onle once per screen, make it in the ViewModel's construcor (for example).
init {
callAPI()
}
Please read more about this here.

onRetainCustomNonConfigurationInstance deprecated in AndroidX

onRetainCustomNonConfigurationInstance was deprecated in AndroidX when the first version got released back in 2018! As the AndroidX Activity library says in the release notes:
onRetainCustomNonConfigurationInstance has been deprecated. Use a ViewModel for storing objects that need to survive configuration changes.
I just want a single object to survive configuration changes, this was the purpose of onRetainCustomNonConfigurationInstance!
For my use case, I want an instance of a Dagger graph to survive configuration changes in an Activity but using AAC ViewModel doesn't feel right to me for that use case. Dagger injects my ViewModels, I don't want to wrap my graph in another ViewModel just for the sake of making it survive configuration changes.
Is there any other way I can make an object survive configuration changes?
Using ViewModel is the most accurate and recommended way to make an object survive configuration changes, you should use it. You could've used onSaveInstanceState but that'd force all the objects needing to support Parcelable, that's not only reasonable, it's sometimes impossible.
To replace onRetainCustomNonConfigurationInstance, you can use the same APIs that support ViewModel in a way that handles all the complexity for you.
You can use this implementation of LongLastingElement API (code here) that uses ViewModel under the hood and removes all the boilerplate code to make an object survive configuration changes.
For your example, to make the Dagger graph survive configuration changes with this API, the code would look like this:
class LoginActivity : AppCompatActivity() {
private lateinit var loginComponent: LoginComponent
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loginComponent = LongLastingElement.getInstance<LoginComponent>(this).getElement {
(applicationContext as MyDaggerApplication).appComponent.loginComponent().create()
}
loginComponent.inject(this)
}
}
LongLastingElement has a static method called getInstance where you specify the type of the object that needs to be stored and you pass a lifecycle owner in. Then, the getElement method is called, it receives a lambda as a parameter that needs to create an instance of the object you want to store.
Since the Lifecycle owner is used to obtain the LongLastingElement instance, the lambda that getElement takes as a parameter will be only called once. It'll create an instance of the object the very first time is called and the same instance will be reused after configuration changes and subsequent calls to getElement. This works for any lifecycle owner such as Activities and Fragments.

Android Architecture Components: ViewModel keeps getting re-initialised

I have an activity that uses the ViewModel architecture component:
class RandomIdViewModel : ViewModel() {
var currentId : MutableLiveData<String?> = MutableLiveData()
init {
currentId.value = UUID.randomUUID().toString()
}
}
And then in my Activity I have this in the onCreate() method:
viewModel = ViewModelProviders.of(this).get(RandomIdViewModel::class.java)
viewModel.currentId.observe(this, idObserver)
Every time I rotate my phone the Id changes. So I am fairly confused as to why init is being called when I set the viewModel object.
EDIT
I have been looking at the saving state UI guidelines and it definitely appears that the ViewModel should maintain it's data throughout simple configuration changes:
ViewModel is ideal for storing and managing UI-related data while the user is actively using the application. It allows quick access to UI data and helps you avoid refetching data from network or disk across rotation, window resizing, and other commonly occurring configuration changes. ...
ViewModel is ideal for storing and managing UI-related data while the user is actively using the application. It allows quick access to UI data and helps you avoid refetching data from network or disk across rotation, window resizing, and other commonly occurring configuration changes
It appears that having a global variable in the activity that is stores a reference to the ViewModel as a once off causes the issue. All the examples seem to use the VM in a local variable, which doesn't work for me (I don't want my observers to be declared inline as it starts making the code quite messy1). The local variable seems to get a new instance every time a config change occurs. However if I create a method:
private fun viewModel() = ViewModelProviders.of(this).get(RandomIdViewModel::class.java)
and I call this whenever I need the VM. I think this is a bug that will most likely be resolved in the future.
1As a side note I also need to point out that I also had to remove my observers when the activity was not using them. This was another reason why I couldn't just inline the definition of the observers as they happen in different lifecycle events:
override fun onResume() {
super.onResume()
viewModel().currentId.observe(this, idObserver)
}
override fun onPause() {
viewModel().currentId.removeObserver(idObserver)
super.onPause()
}

Context in MVVM in android

I have started working on MVVM architecture for android application.I have a doubt that is it right to pass the context to view model ? If not then how can my view model can access the context if needed.
I am doing the following things:
Feed data using some EditText.
Send this data to View model.
View model send this data to repository
Repository storing this data to shared preferences of the device.
As shared preferences required context to instantiate the object.
I am new to this architecture any guidance would be helpful for me, thanks in advance.
I think the use of ApplicationContext is ok, You can extend your ViewModel from AndroidViewModel and whenever you need a reference to the context use getApplication() methods.
Even better, if your using dagger, you don't need this at all, you just inject your ApplicationContext where ever you need it. It can be in your view model or a utility class that handles shared preference, etc.
UPDATE 2019
I don't want to mislead anyone with my old post, so figured I better update it.
With the latest release of the architectural components and JetPack, MVVM is now a real competitor for actual proper structure of breaking apart the code base in a very clean way.
The View is the Activity or Fragment along with the xml that is inflated basically (that's a bit of a weird MVVM, I know, but let's roll with it).
The ViewModel is the actual ViewModel classes offered now with lifecycle scopes and observers. Due to these new features, and life cycle management tools, the ViewModel is very nice at maintaining the data for the view while accessing the Repository Layer of Room DB or Retro API for fetching appropriate LiveData, Observable Data or just standard Models.
I'll leave this here as I feel it was very relevant during the earlier pre Architecture Implementation Days, but if you are not using the Architecture Components and JetPack, Boy are you missing out lol. Code is getting so much cleaner and smaller. :)
OLD REPLY
This is a long debated discussion among the Android community. When we refer to MVC it is obvious that Models (aka data holding objects) are the M, Controllers = C for the Activity classes (in iOS they actually call them UIViewControllers) and the V for the View in Android is the XML file itself. Now some would argue the architectural breakdown of what represents what. However, let's move past this and discuss MVVM.
MVVM has been around for many years now. There have been a few attempts in the past to bring it to Android through 3rd party binding tools, but it ends up more hacky then it is worth.
Recently with the release of native Data Binding Android is finally capable of doing a somewhat clean implementation of MVVM. So there are a couple options here. You can do
M = Models (data holding objects)
V = Views (the XML file itself representing the UI)
VM = This is where debates occur.
Some say in order to be a "true" ViewModel one must have true separation from the Presentation layer and the Activity class itself has lifecycle callbacks thus making it unworthy of being known as the ViewModel.
Others would point out that in order to handle most of the actions triggered from the ViewModel one must have an awareness of onActivityResult, onNewIntent, onBroadcastReceived, onPause or other life cycle handlings to appropriately manage the user experience. So On my team we consider the Activity as the ViewModel. Otherwise you are passing the activity down to a viewmodel and tightly coupling the two anyways, making a giant hideous maintenance nightmare of code.
So you if you want my opinion, stick to treating the Activity as your ViewModel. Get your data to the ViewModel just like any other binding technology like INotifyPropertyChanged in WPF. You do it through your Binding.
I do two things to make this happen. One I have an Activity variable in the XML layout to inject in the onCreate for the binding setup which gives the XML direct binding rights to any observable properties in the viewModel aka the activity. Then I inject whatever variables I need to make use of as well for example you may have a WeatherModel that populates a forecast that lives within the Activity that you would also set in the onCreate to allow the XML access to both it's ViewModel (aka activity) and it's viewModel's Objects.
The other alternative is to make a ViewModel object and inject it in the XML in the onCreate of your Activity and then continue to call back and forth from the activity to the viewmodel for handling actions and lifecycles and feel free to manage that nightmare haha, I did an entire project this way and by the end of it, I redid it all to avoid all the duplication of coding efforts and hideous back and forths.
Goodluck and I hope that helps.
A very good question, and it is not as simple as it seems!
You can see an example from Google team here.
They solved the problem with the help of the factory.
There it is pass (of course) the Application context (not Activity context !).
A small problem - and so much boilerplate code!
My decision:
public class MainApplication extends Application {
public void onCreate() {
AppSharedPref sharedPref = AppSharedPref.getInstance(PreferenceManager.getDefaultSharedPreferences(this));
AppRepository.getInstance(sharedPref);
Repository is singltone (a lot of code is skipped for brevity):
public class AppRepository implements AppDataSource {
public static AppRepository getInstance(#NonNull AppSharedPref sharedPref) {
if (INSTANCE == null) {
INSTANCE = new AppRepository(sharedPref);
}
return INSTANCE;
}
In ViewModel call:
public class MyViewModel extends AndroidViewModel {
// constructor
public MyViewModel(#NonNull Application application) {
repository = AppRepository.getInstance(.....);
}
Look into Dagger 2!
That's true , you definitively shouldn't pass Activity to your xml or ViewModel. It will make your ViewModel no better than these 2000-line activities that we are trying to move away from with this architecture.
The solution we have in our MVVM project is to inject SharedPreferences with Dagger. You can of corse use ApplicationContext as well but do you need multiple instances of SharedPreferences in the project??
We a have a utility class for SharedPreferences and it is nice to keep it a singleton and inject wherever you need it.
I use MVVM in my application. I always try not to use Context inside my View Model. I also encountered the problem with SharedPreferences requiring a context to access the preference files. One of my solutions without using Dagger is to create a Preference utility class that will have reference to the application context. You initialize this utility class in you Application class. You get reference to the shared preference through a public static method provided by the utility class. You can directly call the utility class from your repository class. I prefer to contain all logic related to data storage in the repository class that's why i call the sharedpreference utility class in my repository class.
PreferenceManager.java
public class PreferenceManager {
private static SharedPreferences mSharedpreferences;
private PreferenceManager() {}
public static void initialize(Context context) {
mSharedpreferences= context.getSharedPreferences(context.getPackageName(),
Context.MODE_PRIVATE);
}
public static SharedPreferences getSharedPreferences() {
return mSharedpreferences;
}
}
App.java
public class App extends Application {
#Override
public void onCreate() {
PreferenceManager.initialize(this);
}
}
Repository.java
public class Repository {
public void someMethod() {
PreferenceManager.getSharedPreferences.edit.putBoolean("sample", true).apply();
}
You should use AndroidViewModel() in the ViewModel, pass application:Applicatoin, and use getApplication() to get the context.
ViewModel Example:
class MainTrendsViewModel (application: Application) : AndroidViewModel(application) {
// ViewModel must extend AndroidViewModel(), and pass application to it.
// Code here and
// use getApplication() to get context
}
Other anwer that work for me that you could reference: reference answer that works for me
You should use AndroidViewModel class or keep reference to Application context in yours ViewModel implementation in this case.
ViewModel class was designed to keep data persistent between different instances of Activity through its lifecircle and storing reference to one of Activity instance context really doesn't make sense.

Categories

Resources