android MVVM without Databinding - android

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

Related

Is this proper Android MVVM design?

I'm a beginner with Android development, and I'm trying to make a practice app using MVVM. It's my first experience with this architecture, so I'm pretty unsure of everything that I do so far.
I'm trying to programmatically set the CollapsingToolBar's scroll flags. When the RecyclerView's list is empty, I'd like to disable the scrolling effect. When it has items, I reenable it.
In my ViewModel, I have:
#HiltViewModel
class MealsViewModel #Inject constructor(
private val mealDao : MealDao
) : ViewModel() {
\\ depending on the calendar day, grab the list of meals from the Room Database
private val currentDay: MutableLiveData<Date> = MutableLiveData(Date())
val meals = Transformations.switchMap(currentDay){ date -> mealDao.getMeals(date).asLiveData() }
\\ this is observed in the fragment; if meals is empty from database, return this int (scroll flag)
val enableOrDisableScroll = meals.map {
if (it.isNullOrEmpty()) AppBarLayout.LayoutParams.SCROLL_FLAG_NO_SCROLL else
AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP or
AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or
AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
}
In the Fragment, enableOrDisableScroll is observed like so:
viewModel.enableOrDisableScroll.observe(viewLifecycleOwner) {
val params: AppBarLayout.LayoutParams = collapsingToolBar.layoutParams
as AppBarLayout.LayoutParams
\\ it is the returned value from the ViewModel observation
params.scrollFlags = it
collapsingToolBar.layoutParams = params
}
(This solution is modified from https://stackoverflow.com/a/32699543/11813571)
Is this proper MVVM design? Is all the "business logic" properly separated from the view (Fragment)? I may be having trouble understanding the term itself. In Android programming, is business logic anything that would decide how a view is created and UI updated? How could my MVVM approach be improved? It works as intended, but I don't know if it's optimal.
Thank you!
This is how Basically MVVM pattern looks like .It is quite simple , the View (Fragment , Activity , Custom Views ) should only communicate with the ViewModel , it shouldn't call any other classes . The ViewModel acts as a middle-men for your data and the views . So the data should be passed from your datasource (which looking at your code seems to be room database ) to the viewModel and from there to the View .For passing the data from ViewModel to the views, one should make use of LiveData /StateFlow /SharedFLow . Now , the data in the viewModel should not come directly from DataSource if you want to follow MVVM strictly . There has to be another layer called Repository between your datasource and viewModel as shown in the diagram . In your code , in ViewModel you have directly called datasource in the form of (dao) , that is not permissible . The repository acts as a middle-men for data to flow between the datasource and the ViewModel . This is how MVVM works .
Answers to your Questions :
1.Is this proper MVVM design :
The ViewModel -Fragment logic has been handled well but you have directly called dao in your viewModel which is faulty . You have to create a Repository , where you have to call Dao and have to pass that repository in the ViewModel .
What is business logic ?
The answer will partly be a matter of the project's complexity and of developer's taste . But make sure that it does not have any View related code in it and it should flow via ViewModel . The business logic should be reusable and should be kept away from other classes

Comparison between Layout Binding and Manual Binding(with observe) in MVVM architecture of android

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.

Best practice to combine android ViewModel and data binding

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.

LiveData or DataBinding Observer

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.

When to use Android’s LiveData and Observable field?

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}"
/>

Categories

Resources