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.
Related
In MVVM architecture, should the view model only return Live data to fragment?
Is it ok to return other primitive data type other than Live data also?
You could return the data type that you need but in most cases you need to return Live data or StateFlow ( which is similar to Live data )
Because you want that observe in your fragment to get notified about any change.
There is no difference is term of architecture between LiveData<String> and String but it's just about what you need, if you need data that you can observe and change the UI of your fragment depending on that data use Live data and if you just need to access another primitive data type for some reason, it's totally fine.
It depends on your case.
As we know ViewModel is the recommended UI-State holder, it maintains the state while changing the view configuration.
We usually use observable types LiveData, RxJava2, and Flow, so if any changes come from the data or domain layers or even from another view like "SharedViewModel" the UI will be continuously updated.
In most cases, we use observable types in the view model and non-observables in the view itself.
We can see this UI event decision tree in the documentation.
which helps us to configure where should we put the data.
.
for more details.
UI events
State holders and UI State
Using Android Jetpack components and MVVM architecture, we can get live data updates in a View from a View Model in 2 ways, one is to bind the layout with the live data variable, other way is to observe the variable in code.
To illustrate my question I have taken an example. Suppose there is a view model interface getTimeString() which returns the current time.
a) Layout Data Binding
The view in the layout looks something like this
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
app:binder_data_date="#{sampleVM.timeString}"/>
The binding adapter looks something like this
#BindingAdapter("binder_data_date")
public static void binder_data_date(TextView text, String data) {
text.setText(data);
}
b) Manual Data binding (just to give it a name):
In Manual data binding, there is nothing in the layout view with respect to the binder, and I observe the live data using the observe() and update the textview.
FragmentSplashScreenManualBindingBinding fragmentSplashScreenBinding;
SampleViewModel sampleViewModel1 = ViewModelProviders.of(this).get(SampleViewModel.class);
public void onSomeRandomFunc(...) {
....
sampleViewModel1.getTimeString().observe(getViewLifecycleOwner(), data -> {
fragmentSplashScreenBinding.sampleText.setText(data);
});
}
I know the first method is much easier to use and both works.
But is using the second method and the way to access the variable (fragmentSplashScreenBinding.sampleText.setText()) in fragment to update the View correct?
Will the performance get impacted if I use the second method?
Your manual Data binding is not incorrect and doesn't have a significant impact on the performance but you will lose two benefits:
Null pointer exception handling: Layout Data Binding handles null data and you don't need to check null objects to prevent app crash when you want to extract data objects and pass them to views.
Code Reusability: If you want to use your layout in different Activities, with
Layout Data Binding you just need to pass the data variable to the layout. But for Manual Data binding you should copy the same code for each java class to assign variable to views which will make a lot of boilerplate code in complex views.
Moreover, If you are using data binding to replace findViewById() as your second method there is a better way called View Binding which you can read more about it here.
Instead of answering your 2 points in post directly - let me mention few key features of both data binding and Live data - which may eventually help you choose 1 over the other.
Live data class supports Transformations - this useful class provide a way to apply any changes to be done to the live data object before dispatching it to the observers, or you may need to return a different LiveData instance based on the value of another one. Below is a sample example of applying the Transformation on LiveData from Official android docs,
class ScheduleViewModel : ViewModel() {
val userName: LiveData
init {
val result = Repository.userName
userName = Transformations.map(result) { result -> result.value }
} }
As you can see from above example - in the "init" the LiveData Object is "transformed" using Transformations.map before dispatching its content to "observers"
Data binding is mostly works with set of Observables and cannot "transform" the data under observation before dispatching like in above example.
Another useful feature of with LiveData is a class called MediatorLiveData - this subclass which may observe other LiveData objects and react based on changes to it - With data binding AFAIK its very much restricted to a specific Observable Fields.
I am a little confused about how to combine 2 techniques in android, namely
ViewModel (https://developer.android.com/topic/libraries/architecture/viewmodel) and
Data Binding Library (https://developer.android.com/topic/libraries/data-binding)
ViewModel should handle business logic, the layer behind the actual view and send data to the view with something like LiveData. The view observes to this LiveData and updates itself on changes
Data Binding Library exists to make it easier to bind to the view and interact with the view on another level (for example by updating some properties of some class)
The questions:
Should the properties / model property of Data Binding Library be kept inside of ViewModel class (A) or in the view (activity, fragment) (B)
If (A) : If the Data Binding Library properties / models are kept in ViewModel class, is it considered bad practice that view logic is executed inside ViewModel by changing data from the data binding library?
Is there a good code example (some GitHub repo) where there is an example of a decent combination of those 2 concepts?
Update: Found official documentation for my issue. Here is the link:
https://developer.android.com/topic/libraries/data-binding/architecture#viewmodel
How data binding works
Consider using LiveData, it lives inside the ViewModel and is how the data binding library knows that you must update for example the string of a TextView.
What data binding actually does is something similar to what you would explicitly do in your fragment:
Subscribe from your Kotlin code (Fragment/Activity) to a LiveData property that lives within the ViewModel but in this case, data binding will update the view values for you since you will indicate it before from your XML Layout.
So the answer is (A):
You could have a ViewModel class with properties of type LiveData<T> and from your Layout, you can use them directly without subscribing explicitly from your kotlin code as I mentioned before, which continues to guarantee that the ViewModel continues being the provider of information for the user's view, the difference is that instead of you are doing it explicitly, data binding will do it for you.
class MyViewModel : ViewModel {
// view model doesn't know if Fragment/Activity is using data binding or not, it just continues providing info as normal.
val myString : MutableLiveData<String> = MutableLiveData()
init {
myString.value = "a value that data binding will print in a TextView for you"
}
private fun changeMyString() {
// Change the value in the future when you want and then data binding will print the text in your TextView for you.
myString.value = "other value to that TextView"
}
}
Layout:
<TextView
android:text="#{myViewModel.myString}" />
Resources
This Google Codelab is pretty useful, it helped me when I started with data binding because it is prepared to teach.
If you just want to go directly to code, android/sunflower is a repository that uses data binding and in general provides useful samples of jetpack features.
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.
Question : Can I implement android app with MVVM without using Databinding.
Problem I am trying to solve is pretty simple:
read a list of items from backend API and show them in a Recylerview.
How I am implementing:
In the View - I have Activity and RecyclerViewAdapter
Model : ApiResponse and data models
network - retrofit API service, RxJava2
for ViewModel part - I have a ViewModel class(that doesn't derive from anything) that basically calls Retrofit Service and gets data using RxJava calls.
ViewModel has calls such as :
void getItems();
void addItemData();
void removeItem();
which call service with RXJava2 as
ObServable<ApiResponse> getItems();
ObServable<ApiResponse> addItemData();
ObServable<ApiResponse> removeItem();
View instantiates ViewModel object.
ViewModel gets the instance of Adapter object during creation.
In the View, clicking a button calls a ClickHandler in the Activity which calls a ViewModel#getItems() method. Since ViewModel has link to Adapter, the viewModel updates the items in the adapter so that RecyclerView is automatically updated.
I am not sure if this is right approach for MVVM.
Databinding seems a bit like spaghetti to me.
Again, can we implement MVVM in android without DataBinding ?
Is the approach OK?
Yes! You can. But i think your approach can be better.
Remember that the view model must not have a reference to your view.
ViewModel expose observables, and in your view, you should observe those observables and react over changes.
You can have something like this:
Note: This example is with Kotlin and LiveData because, why not? But you can take this and use it with Java & Rx
ItemsViewModel : ViewModel() {
private val items = MutableLiveData<List<Items>>()
fun getAllItems() : LiveData<List<Items>> {
return items
}
//..
}
ItemsActivity : Activity() {
private var itemsAdapter: ItemsAdapter? = null
private var viewModel: ItemsViewModel? = null
override fun onCreate(savedInstance: Bundle) {
// ...
// Create your Adapter
itemsAdapter = ItemsAdapter()
recyclerView.adapter = itemsAdapter
// Create and observe your view model
viewModel = ViewModelProviders.of(this).get(ItemsViewModel::class.java)
viewModel.getAllItems().observe(this, Observer {
it?.let {
adapter?.datasource = it
}
}
In this case, the view observes view model, and notify the adapter. Then in your adapter, you do the bind as usual, without databinding.
Definitely possible, it's totally up to you how you interpret the "binding" part of MVVM. In our team, we use MVVM with RxJava instead of Android Data Binding.
Your ViewModel has an interface with outputs and inputs like this:
interface TasksViewModel {
// inputs
Observer<Task> taskAddedTrigger();
Observer<Task> taskClickedTrigger();
Observer<Task> taskCompletedTrigger();
// outputs
Observable<Boolean> isLoading();
Observable<List<Task>> tasks();
}
Your ViewModel then just uses RxJava to map inputs to outputs in a very functional style.
You Fragment supplies Inputs to the ViewModel whenever User input is received. It subscribes to Outputs and updates the user interface accordingly when the ViewModel's Output changes.
Here is a blog post which covers the topic in detail (Disclaimer: I wrote it)
The distinguishing characteristic of MVVM is that the ViewModel is not directly coupled to a View (indeed, you could bind your ViewModel to different layouts). This also has implications on the ease of unit testing. By having a reference to the Adapter, it is technically more like MVC. You don't have to use databinding, but for true MVVM, I think you would need another Observer Pattern mechanism for the View to be notified of changes so that it could pull the data it needs.
Your saying Since ViewModel has a link to Adapter and that is the problem because ViewModel should not have reference to view and In your adapter, you have views so by doing this your not following MVVM at all!!
You can still use MVVM without data binding but you need some way to notify the view about data changes, It can be LiveData (preferred way), Java Observable, Rx or even a custom implementation. The view will get notified about the changes and updates itself, in your case, view will update the adapter.
see my answer here for an example Are actions allowed in MVVM? Android
I think you should use data binding to notify the data changed from network or database, your viewmodel should expose methods for requiring or updating data, when the data arrived you can do some operation on your data, and post them to your container(activity or fragment), in there you can update your RecyclerView and its adapter