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.
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)
Was just wondering, why is it better to use backing property for MutableLiveData, instead of just exposing getter function that returns the MutableLiveData property as live data. For example:
Why this code
private val _registeredDevicesObservable: MediatorLiveData<List<Data>> = MediatorLiveData()
val registeredDevicesObservable: LiveData<List<Data>> = _registeredDevicesObservable
is better or more acceptable than this one
private val _registeredDevicesObservable: MediatorLiveData<List<Data>> = MediatorLiveData()
fun registeredDevicesObservable(): LiveData<List<Data>> = _registeredDevicesObservable
As also when this getter function is keeping the LiveData immutability and keeps me from having that little annoying underscore syntax when accessing the property inside the view model.
It's just less idiomatic in the language to use a function that simply returns an already-available object. You're free to do it any way you like, but if others have to work with your code, it will be easier to understand and work with if you follow general conventions.
There isn't as strong a convention about whether the backing property should have a leading underscore in the name, so if you don't like it, don't use it.
One reason to stick with these conventions is there is a proposed upcoming language feature to allow you to do this without a backing property, and so if you're following the conventions, it will be very easy to update your code to eliminate the backing property.
Given this scenario:
Model
data class User(var id: int, var name: String)
View Model
val Users: LiveData<List<User>>
val SelectedUser: LiveData<User>
fun changeSelectedUserName(){SelectedUser.Name = "foo"}
UI
<android:TextView Text="#{viewmodel.SelectedUser.name}"/>
<android:Button Text="Change!" onClik="#{() -> viewmodel.changeSelectedUserName()}"/>
When user clicks 'Change!' button the textview won't change because the 'name' field is not LiveData.
Questions
Should data class re-expose its fields as LiveData too?
If so, what will happen to regular fields? Are they replaced or keeped with another naming convention?
What is the correct naming convention if I'm using retrofit? So I can keep both the interface methods and LiveData working with the less amount of code?
Your issue is that you are modifying the User object properties directly, rather than update the LiveData.
For this to work, you have to do one of the following:
Make User extend BaseObservable, and invoke notifyPropertyChanged(BR.name) when the name property changes, and remove the LiveData<User>.
Make User extend BaseObservable, and put #Bindable annotation on the property getters, and remove LiveData<User>.
Make User properties val, and to make a modification, create a new User instance with the changed properties, and set it as value of the MutableLiveData<User>.
Ditch SelectedUser entirely, and replace it with two ObservableFields, one for selectedId: ObservableInt, and one for selectedName: ObservableField<String>. Now you can modify the values in place and also create bindings against it from databinding.
Remove databinding and use viewbinding instead, now you don't have to worry about notifying the databinding framework of property changes. 😏
Should data class re-expose its fields as LiveData too?
No
I am referring to the Plaid open source project on github Link to Plaid
It is an excellent source to learn new techniques on Android.
As I was going through the code, I came across a certain style of coding around LiveData which I really didn't understand. If anybody can help me get it.
Here goes:
There's a ViewModel(vm) with this piece of code:
private val _openLink = MutableLiveData<Event<String>>()
val openLink: LiveData<Event<String>>
get() = _openLink
Fairly simple? Note that, there are 2 variables here: openLink and _openLink. The getter for openLink is returning the _openLink LiveData.
In the activity they observe the openLink LiveData as follows:
viewModel.also { vm ->
vm.openLink.observe(this, EventObserver { openLink(it) })
..... // Other stuff
}
Now, the other livedata _openLink is being called by the UI supposedly on a button click and it's defined like this:
fun viewShotRequested() {
_shotUiModel.value?.let { model -> // ignore this part
_openLink.value = Event(model.url) // setValue on _openLink
}
}
So here my understanding is, on setValue() on _openLink, EventObserver{openLink(it)} will get called.
My question is, why have they done it like this?
Questions:
Why not observe directly on _openLink?
Will it not have the same effect? What am I missing here?
_openLink is mutable. You must always expose something which is immutable and cannot be changed by your observer, because that should only be done by your ViewModel, even though exposing _openLink would have no effect.
That's why you need to expose openLink which is immutable.
private val _openLink = MutableLiveData<Event<String>>()
val openLink: LiveData<Event<String>> = _openLink
The MutableLiveData property shouldn't be exposed: it is mutable and could be changed anywhere across your program.
That's why the LiveData is exposed instead: it is responsible for updating your property, and it uses the MutableLiveData as a backing field.
The exception to this would be two-way DataBinding, where direct access to the value would be needed.
My question is, why have they done it like this?
Because Google apparently enjoys writing more code for the sake of writing more code.
The supposed answer if you're inclined to believe them is that LiveData<Event<T>> is preferred over SingleLiveData because they came up with it later than with LiveData<Event<T>> and is therefore supposedly better.
They intend to use an "enqueueable event-bus that forgets items after it is emitted to at least 1 observer", but Jetpack offers no such concept out of the box, personally I had to write one of my own.
Even so, there's a distinction between whether you can write into something, and whether you can only read from something but not write into it yourself. In this case, only the ViewModel wants to be able to emit events, so it is the ViewModel that holds a Mutable__ reference, but exposes a regular LiveData to the outside world (in my case, EventEmitter vs EventSource).
As for the _, it's a Kotlin style convention that will hopefully be changed one day, as you could either do:
private val _openLink = MutableLiveData<Event<String>>()
val openLink: LiveData<Event<String>>
get() = _openLink
But you could also do
private val openLink = MutableLiveData<Event<String>>()
fun openLink(): LiveData<Event<String>> = openLink
And that way we could ditch the _ prefix in our own code, but for some reason the authors of Kotlin hadn't come up with that convention in time.
In many samples i see that:
class DataViewModel{
val data:LivaData<Int>
get() = _data
private val _data = MutableLiveData<Int>()
}
But more simply looks like this:
class DataViewModel{
val data = MutableLiveData<Int>()
}
so, why need this more complicated code construction with 2 fields?
It's a practice designed to restrict modification of the value from outside the class.
LiveData is read-only.
MutableLiveData, as the name implies, allows one to change the value it holds.
If you expose a MutableLiveData directly, like in your second example, any code which can access that data field could also modify the value it holds.
Exposing the ability to change data's content from outside DataViewModel class could make it harder to debug and reason about where data's content is coming from at any given time.
MutableLiveData is essentially a LiveData with public access to two methods setValue() and postValue() for modifying that data.
Therefore, MutableLiveData is needed if you plan to modify the values of the LiveData.
However, in programming, it's a common concept to make your variables immutable or restrict the access of those who can modify the data of an object. You wouldn't want to expose the ability to modify the contents of variables within an object if there's no need to do so.
Therefore, for MutableLiveData, we normally use a getter to get it's parent form, which is LiveData.
By getting only LiveData, we can ensure that those who access the LiveData object can only read the values stored within with no ability to change them.
In a sense, it's just the concept of why you should use private variables with getters.