I want to understand the difference between MutableLiveData vs ObservableList in Android ViewModel.
val questions: MutableLiveData<List<Question>> = MutableLiveData()
val options: ObservableList<Option> = ObservableArrayList()
The main difference here is that ObservableList is designed for DataBinding while MutableLiveData for data change observation that is made from Activity or Fragment, which means that MutableLiveData takes into consideration a LifeCycle of a component and will not call it if isn't in active state.
You can use ObservableList from your code of course, but, for example, it will not hold last passed data, unlike LiveData.
Related
I recently started with the ViewModel and AndroidViewModel, I see there are different approach to initialise a viewModel instance, for me all works fine, I just want to know which one to use when? and where should I initialise the viewModel object? following all are the different approach to get the viewModel instance and works for me:
val myViewModel1 = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
val myViewModel2 = ViewModelProvider.AndroidViewModelFactory(this.application).create(MyViewModel::class.java)
val myViewModel3 = ViewModelProvider(this).get(MyViewModel::class.java)
val myViewModel4: MyViewModel by viewModels()
val myViewModel5 by viewModels<MyViewModel>()
The easiest and most simple for me are 3rd, 4th and 5th, however I don't know what is the difference in all the five approaches, also please let me know if there any other way or optimal way to initialise my viewModel object, I do the initialisation on the global variable while declaring it, is it okay to initialise at the declaration time or it should be done inside some lifecycle method?
In case anyone looking for in depth answer, please check this, here we have the following way to create or get the viewModel object:
val myViewModel1 = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
myViewModel2 = ViewModelProvider.AndroidViewModelFactory(this.application).create(MyViewModel::class.java)
val myViewModel3 = ViewModelProvider(this).get(MyViewModel::class.java)
val myViewModel4: MyViewModel by viewModels()
val myViewModel5 by viewModels<MyViewModel>()
All do the same thing, the only two key differences is:
The viewModel initialisation with lazy loading and without lazy loading.
The viewModel with multiple parameter and no parameters.
Lets see this wrt the lazy loading and without lazy loading, the first three are without the delegate by that means there is no lazy loading of that object, so it's the developer
responsibility to create the viewModel object only when activity is created or the fragment is attached to the activity, that means the first three approach(1, 2, 3) can't be
used at global scope, if used at global scope the variable must be
a var with lateint or null initialisation, and the
initialisation(approach 1, 2, 3) must happen in the onCreate or
onViewCreated(in case of fragment).
Therefor the best way to create the viewModel object is using the delegate by(4, 5), both are same with a bit different syntax, I choose 4 because of it's simplicity and readability.
val myViewModel4: MyViewModel by viewModels()
The by delegate gives the flexibility to lazy load the instance and you can define the viewModel at global scope and get ride off the boilerplate code, if you try to initialise the viewModel at global scope without the delegate the app will crash since the viewModel will try to initialise before the activity is created(it will not lazy load the viewModel instance).
Now let's see how to lazy load with multiple parameters, the 6th approach not mention in the question.
If you have multiple parameters in your view model and not using any dependency injection, you can use a ViewModelFactory implementation and then lazy load it:
val myViewModelWithParm: MyViewModel by viewModels { MyViewModelFactory(application, "param1", "param2") }
ViewModelFactory implementation:
class MyViewModelFactory(val application: Application, val param1: String, val param2: String) :
ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MyViewModel(application, param1, param2) as T
}
}
Till this point we are clear on the delegate initialisation(4, 5), and how it is different with(1, 2, 3) now let's see the difference on the top 3 approach(1, 2, 3).
Let's first check 1 and 2.
val myViewModel1 = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
myViewModel2 = ViewModelProvider.AndroidViewModelFactory(this.application).create(MyViewModel::class.java)
The key difference in them is one uses ViewModelProvider.NewInstanceFactory and other uses ViewModelProvider.AndroidViewModelFactory, so I checked the source code of both the classes and found that ViewModelProvider.AndroidViewModelFactory is actually the implementation of ViewModelProvider.NewInstanceFactory which override the create function that means both are doing the same stuff, preferable both approach should be chosen if we want multiple parameters however for that we have to override ViewModelProvider.NewInstanceFactory to create our own factory like it's done here
Now comes the third one:
val myViewModel3 = ViewModelProvider(this).get(MyViewModel::class.java)
This is the simple form of 1 and 2 when we don't have multiple parameters in our ViewModel and don't want to lazy load the object.
Note: I highly recommend the approach 4 or 5(both are same with different syntax), since this is the most suitable and optimal to write, if you don't have multiple arguments, in case you have multiple arguments you can use the approach 6 mentioned in the answer by implementing ViewModelProvider.Factory.
3 is the standard way to fetch (and create if necessary) a ViewModel with no constructor parameters. Internally, that's doing 1 to pass a factory that calls the empty constructor (NewInstanceFactory()).
An AndroidViewModel is a subclass of ViewModel that automatically passes in an application reference, in case you need access to things like the application context. So even if your AndroidViewModel has no parameters, the factory that creates it needs to pass in an application, which is what 2 is doing.
This is all taken care of for you by default using 3 - you only need to define and use a factory if your VM needs to be configured with some extra parameters.
4 and 5 are the same thing, just with the type specified in a different place (you only need one declaration and the other will be inferred). They're delegates from the KTX libraries, and they do the same thing as 3, but they're much more readable IMO - especially if you're mixing scopes, like using by viewModels to get a Fragment's own VM, and also by activityViewModels to get the Activity's VM to share data with that and other Fragments.
They're also lazy delegates (as far as I'm aware!) meaning the VM only gets instantiated when it's first accessed, which is generally going to happen later in the lifecycle (instead of when the object is first constructed). I'm not sure if there is a problem initialising the VM on construction, but all of the official examples I've seen seem to fetch it in onCreate (or thereabouts)
I'm learning Kotlin and I'm trying to use the same ViewModel for display a list of users and for edit of a user.
I'm using room so I have a "getPersonnelById() which needs to be Observed. The problem is that I would like to Observe only Once and I don't know how to do...
Here's my function
private fun retrievePersonnelData(id: Long){
if(id != -1L){
val observer = dataSource.getPersonnelById(id).observeForever{
newPersonnel.value = it
Timber.e("Valeur newPersonnel = ${newPersonnel.value}")
}
}
}
I've used as recommended a observeForever but I don't know how to use removeObserver in this case...
Thank you very much
If you need to get data once - consider using suspend functions in Room and get data by demand.
If you need to get a particular Personnel object and observe changes in DB of it, store value of getPersonnelById(id) in LiveData<Personnel> and observe it from Activity/Fragment
observeForever is mostly needed in testing purposes, you should better use observe function to not manually remove an observer every time.
I using MVVM on my Android application, on ViewModel I have many observers (from data binding) like ObservableBoolean, ObservableField, I read that I can use LiveData/MutableLiveData instead this observers... What's the difference? I can replace all my data binding observers by LiveData/MutableLiveData?
eg:
replace:
val loading: ObservableBoolean = ObservableBoolean()
By:
val loading: MutableLiveData<Boolean> = MutableLiveData()
Many times has passed and I learned a lot...
Replace all Data Binding Observable by LiveData, because LiveData respect the Activity lifecycle and can be used in JetPack lib's, like Room, Coroutine...
Depends on where you are reading the data from .
In our current project ,we read directly from RoomDB . RoomDB has the capability to send back a liveData object .
Through your ViewModel , you do a query to RoomDB which returns a LiveData (RoomDB will be your Single Source of Truth)
Your View say an Activity or Fragment - Subscribes to this viewmodel as an observer
And you update the view as per the response returned .
You could also directly bind the xml through Android Databinding(Using LiveData with Data Binding)
Mutable Data is used normally if you have any modifications after retrieving
This is a good Place to Start
If your aim is to just change basic properties of views in xml based on a change with the data in primitive data type in view model, it is simple and easy to use Data binding. For rest of the cases, live data is the only way.
Looking to the code of some Google's demo app (like sunflower or Google io 2018 app) and I've noticed that for the viemodels' backing properties they use a separate instance of the same type with a custom getter; like this:
private val _userData: MutableLiveData<User>
val userData: LiveData<User>
get() = _userData
but why do they do that? Isn't better to directly make the _userData accessible?
Could it be because while _userData is a MutableLiveData they don't want the observer to be able to change the value?
userData which is exposed to the Activity or Fragment must be immutable since the view only needs to observe to the LiveData. So, we need to make the actual _userData returns a LiveData.
One way is using the Kotlin coding convention and create two variables, _userData and userData, one is mutable and another one is not:
If a class has two properties which are conceptually the same but one
is part of a public API and another is an implementation detail, use
an underscore as the prefix for the name of the private property.
I’m implementing a MVVM and data-binding and I’m trying to understand when should I use Observable field over LiveData?
I already run through different documentations and discovered that LiveData is lifecycle aware, but in sample codes in Github these two are being used in ViewModel at the same time. So, I’m confused if LiveData is better than Observable field, why not just use LiveData at all?
Both have their use-cases, for instance:
If you want a life-cycle tolerant container for your UI state model, LiveData is the answer.
If you want to make the UI update itself when a piece of logic is changed in your view model, then use ObservableFields.
I myself prefer using a combination of LivaData and ObservableField/BaseObservable, the LiveData will normally behave as a life-cycle aware data container and also a channel between the VM and the View.
On the other hand the UI state model objects that are emitted through the LiveData are themselves BaseObservable or have their fields as ObservableField.
That way I can use the LiveData for total changes of the UI state.
And set values to the UI state model ObservableField fields whenever a small portion of the UI is to be updated.
Edit:
Here is a quick illustration on a UserProfile component for example:
UIStateModel
data class ProfileUIModel(
private val _name: String,
private val _age: Int
): BaseObservable() {
var name: String
#Bindable get() = _name
set(value) {
_name = value
notifyPropertyChanged(BR.name)
}
var age: Int
#Bindable get() = _age
set(value) {
_age = value
notifyPropertyChanged(BR.age)
}
}
ViewModel
class UserProfileViewModel: ViewModel() {
val profileLiveData: MutableLiveData = MutableLiveData()
...
// When you need to rebind the whole profile UI object.
profileLiveData.setValue(profileUIModel)
...
// When you need to update a specific part of the UI.
// This will trigger the notifyPropertyChanged method on the bindable field "age" and hence notify the UI elements that are observing it to update.
profileLiveData.getValue().age = 20
}
View
You'll observe the profile LiveData changes normally.
XML
You'll use databinding to bind the UI state model.
Edit: Now the mature me prefers Immutability instead of having mutable properties as explained in the answer.
You can use LiveData all the time, as long as there is a LifecycleOwner to observe. I prefer to keep bound fields that are only relevant to the ViewModel as Observable and use LiveData for fields whose state changes are also relevant to the Activity or Fragment.
LiveData - use with LifecycleOwner like activity or fragment
Observable - use with data binding
#Ahmed Ashraf's answer is misleading. If we are talking about data binding only, data binding itself is lifecycle aware, because we set the lifecycleOwner when we use data binding, and it already checks when the view is active.
binding.lifecycleOwner = viewLifecycleOwner
As a result, it's not necessary to use live data to hold a BaseObservable object, we can directly use the BaeObervable object inside the view model.
Coming back to the question, in one-way data binding, you can use LiveData or ObservableFields or even Stateflow, but in 2-way data binding, I still prefer ObservableFields, because it's more flexible, you can easily write custom logic in the observable field setter, and when the UI changes, it can call the setter to do some additional stuff.
class LoginViewModel : BaseObservable {
// val data = ...
#Bindable
fun getRememberMe(): Boolean {
return data.rememberMe
}
fun setRememberMe(value: Boolean) {
// Avoids infinite loops.
if (data.rememberMe != value) {
data.rememberMe = value
// React to the change.
saveData()
// Notify observers of a new value.
notifyPropertyChanged(BR.remember_me)
}
}
}
And in xml file.
<CheckBox
android:id="#+id/rememberMeCheckBox"
android:checked="#={viewmodel.rememberMe}"
/>