Whose role is processing data in the MVVM pattern?
For example, if you need to display a Date object in the format mm/dd/yyyy on one screen and mm-dd-yyyy on another screen, who's the role of View or ViewModel?
If this is the role of View I use #BindingAdapter,
If it is the role of ViewModel, use Livedata<Date>().map {/* format */ }
What's the better way to the MVVM pattern?
Thank you.
The view model of MVVM is a value converter, meaning the view model is responsible for exposing (converting) the data objects from the model in such a way that objects are easily managed and presented. In this respect, the view model is more model than view, and handles most if not all of the view's display logic.
So conceptually and implementation wise the logic or transformation should be in View Model.
Better in ViewModel. If you switch from DataBinding in the future, your mapping logic will persist in the ViewModel.
I would keep the original "long" timestamp inside the viewmodel and then decide based on requirements where to transform the data into string. One good point to keep the string inside the viewmodel is that it does the transformation only once, and keep the data until lifecycle ends. While the binding adapter will do the conversion everytime you bind the view. At the same time the string will be kept in memory until lifecycle ends.
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.
Most of the MVVM examples are dealing with very simple user interfaces.
But lets say I have an activity with many views to update (i.e. lots of data)
As I read in other places, multiple ViewModel objects is a bad pattern.
So, as I see it there are two solutions for that:
Create a single object (and single LiveData for it), that wraps all other data objects.
But there's a problem with this - each data object that gets updated will cause the whole UI to update.
Create multiple objects (and multiple LiveData objects for it).
It means that I need to observe each LiveData object. Is there a problem with this pattern?
Thanks in Advance!
First Point you mentioned : Yes this is not optimal Pattern to do but if you have small data then, separating LiveDatas is more work for less gains
Second Point you mentioned : Yes this is more optimal, you can have a LiveData object for each View you want to update and observe them all from your activity or fragment. There are no issues in this Pattern.
About Mutilple ViewModels :
Multiple ViewModels Pattern in same Activty/Fragment is also an option if you have too many things(LiveData objects or funcitions) happening in one ViewModel. This is only recommended to make viewModels lighter. So only use this if you are having a large viewModel class
Create ViewModels for discrete types of information.
You could for example have a UserViewModel that deals with all state regarding a User. This means you can use the same ViewModel in another context, without pulling data that might not be necessary (as you would if you had a single God ViewModel).
Create as many LiveData objects as you need to model your view.
It is better to condense the data into logical objects, where possible. If only to keep things manageable.
If you have a User, you should use that for your LiveData instead of having a LiveData for E-mail address, Display name, Age, etc. That will make things much simpler for your data bindings. Try to keep things logically grouped together.
Briefly, the question is: in MVVM (AAC), how can Domain (business logic) manage the display of complex states / data in the View layer?
Now in more detail.
It means that inside the Domain: 1) received, calculated some data that need to be shown; 2) the state has changed, it is necessary to react to this (hide / show a group of widgets, call a new fragment, show / update progress, etc.). And it's harder to do than just show a message or a dialog, or just send LiveData to the RecyclerView.
Therefore, examples like "hello world" or "2 + 2 = 4" do not fit, everything is clear in them. In MVP, this is simply done. But here I was able to find the weak point of MVVM.
Now I did the following.
By means of RxJava2 (as an option, it can be LiveData from AAC) from Domain to View (via ViewModel AAC) an object that contains the type of command (enum) is passed and has a bunch of fields for data for all occasions (different fields for different commands of course ).
And further, View contains a large switch-case, depending on the type of command where all this is handled.
Variant 2. To create a bunch of specific objects, and then in the View will sit a large if-instanceof.
Variant 3. Store data for View in ViewModel AAC (for which it is actually intended), and send from the Domain only the type of command, then View takes all the necessary data from the ViewModel.
Variant 4. A heap (in case of complex UseCases) a specific Observables in Domain and a heap of subscribers in the View.
So: is there (if any) a more elegant way? There may be some architecture pattern. Maybe I'm in vain reflexing, and this is the right way(s).
ps. 1) the "Command" pattern here does not exactly fit, 2) the "State" pattern has already been implemented by me, and it does not solve the problem either.
In MVP, this is simply done. But here I was able to find the weak
point of MVVM.
This is not the weak point of MVVM, it is just a difference between the implementation of MVP and MVVM.
In MVP, you create a bunch of interfaces to let View and Presenter talks to each other;
In MVVM, you create a mediator (e.g. LiveData) to bridge View and ViewModel.
IMHO, you can:
In your UserCase, create a MediatorLiveData A to store the result.
In your ViewModel, create a MediatorLiveData B to observe A (i.e. MediatorLiveData.addSource(A))
In your View, observe B to reflect any UI updates.
You can find a concrete example in iosched18.
Model View ViewModel architecture
The view is the user interface, the layout. In Android, this usually means an Activity, Fragment or ViewHolder and its corresponding inflated XML layout file.
The model is our business logic layer, which provides methods for interacting with data.
The view model acts as a middleman between view and model, by exposing the data from the model via properties and containing the UI state. Also, it defines commands which can be called on events like clicks. View models contain the presentation logic of your app.
In the MVVM architectural pattern, the view and the view model mainly interact with each other through data binding. Ideally, the view and view model should not know about each other. The bindings should be the glue between the view and view model and handle most of the stuff in both directions. In Android, however, they can not really be independent:
you have to save and restore state, but the state is now in the view model.
you need to tell your view model about lifecycle events.
you might encounter situations where you need to call view methods directly.
For these cases, both the view and the view model should implement interfaces, which are then used for communication via commands, if necessary. In almost all cases, however, only an interface for the view model is needed, since the data binding library handles the interactions with the view, and custom components can be used e.g. when a context is needed.
The view model also updates the model, e.g. by adding a new element to the database or updating an existing one. It is also used to fetch data from the model. Ideally, the model should also notify the view model of changes, but this depends on the implementation.
Now, generally speaking, the separation of view and view model makes the presentation logic easily testable and also helps with maintenance in the long run. Together with the data binding library, this means less code and cleaner code.
Example:
<layout xmlns:android="...">
<data>
<variable name="vm" type="pkg.MyViewModel" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="#{vm.shouldShowText}"
android:text="#={vm.text}" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="#{vm::onButtonClick}"
android:text="#string/button"/>
</FrameLayout>
</layout>
When you want to use MVVM architecture, your layouts should only reference one variable, the specific view model for this view, in this case, MyViewModel. In the view model, you provide properties for the layout. This can be as easy as returning a String from a model object or more complex, depending on your use case.
public class MyViewModel extends BaseObservable {
private Model model = new Model();
public void setModel(Model model) {
this.model = model;
notifyChange();
}
public boolean shouldShowText() {
return model.isTextRequired();
}
public void setText(String text) {
model.setText(text);
}
public String getText() {
return model.getText();
}
public void onButtonClick(View v) {
// Save data
}
}
Here we have a text property. As we have an EditText for user input, we can use two-way data-binding, to also have the data binding library save the inputs back to the view model. For this, we create both a setter and a getter and bind the property to the text attribute of our EditText, but this time with a = sign before the bracket, which signals the library that we want two-way data binding here.
Also, we only want to show the EditText when our model says that text input is required. For this, we provide a boolean property in our view model and bind it to the visibility attribute. For this to work, we also have to create a binding adapter, which sets the visibility to GONE when false and VISIBLE when true.
#BindingAdapter("android:visibility")
public static void setVisibility(View view, boolean visible) {
view.setVisibility(visible ? View.VISIBLE : View.GONE);
}
Finally, we want to store the information when a Button is pressed. For this, we create a command onButtonClick() in our view model, which handles interacting with the model. In the layout, we bind the command to the onClick attribute of the Button via a method reference. For this to work directly, our method needs to have a single parameter of type View, just like an OnClickListener. As an alternative – if you don’t want the View parameter – you could also use lambda expressions directly in the layout. As you can see, it’s quite easy and straightforward to use data binding with a view model.
Now, it’s important to remember that we want to put presentation logic in our view model for testability. Avoid putting logic directly in the bindings, even though the data binding library allows it. Don’t forget that you can also have custom binding adapters, which can often simplify things.
I realized that I need to migrate in the direction of the MVI pattern.
And it is necessary to implement "unidirectional data flow".
Very well this solution is described here Reactive Apps With Model-View-Intent - Part2 - View And Intent by Hannes Dorfman
Another interesting solution is the library RxPM -- Reactive implementation of Presentation Model pattern in Android
I'm learning MVP structure and I trying to figure out:
How to pass data between models?
Each model represents one action and if I need to send data from one
model to another one, how can I properly make it? Should I pass data
through presenter, like
firstModel -> commonPresenter -> secondModel
OR
send data between models, like
firstModel -> secondModel?
And what if these models interact with different presenters?
The Model View Presenter pattern, like most architectural patterns, is quite open to experimentation. The important thing to keep in mind is to separate the View from the domain logic and data handling.
Particularly in Android, it's useful to keep the code as far as possible from Framework specific classes like Activities and Fragments.
In my experience, it's best to let Models communicate amongst themselves, as Observers of each other. The same goes for Views and Presenters, or, in general, any component in the same architectural layer.
AFAIK you have to convert your Domain models to your Ui models in presenter so you have to call a method or a constructor like this in presenter:
ModelOne modelOne = new ModelOne(modelTwo);
please consider DRY principals and do NOT assign each field in presenter itself like this
modelOne.title = modelTwo.title;
modelOne.id = modelTwo.id;
but if you want convert a Domain layer model to another Domain layer model you have to do it in other layers. read this for more info:
http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/
"the presenter communicates with model layer, converts the data to UI friendly format, and updates the view"
also you can find above sentence in this link:
http://iyadagha.com/using-mvp-ios-swift/