MVVM - Model View ViewModel
Disadvantages of MVVM:
Communication between various MVVM components and data binding can be painful
Code reusability of views and view model is difficult.
Managing view models and their state in nested views and complex UI’s is difficult
MVC vs MVVM
- MVC architecture has “one to many” relationships between Controller & View
MVVM architecture, “one to many” relationships between View & View Model. - MVC framework is an architectural pattern that separates an applications into three main logical components Model, View, and Controller.
MVVM facilitates a separation of development of the graphical user interface with the help of mark-up language or GUI code - In MVC, controller is the entry point to the Application
In MVVM, the view is the entry point to the Application.
MVP vs MVVM
In MVP, the View and Presenter have references to each other (generally through an interface).
With MVVM, only the View has a reference to the ViewModel.
The ViewModel knows nothing about the View - it simply exposes some observable thing (like LiveData) that the View subscribes to.
Is simpler because the dependencies are easier to manage. It also theoretically makes testing the ViewModel easier because you don't need to mock a View. As far as testing is concerned, we can run ViewModel tests on a local JVM (as opposed to an instrumented test on an real Android device), even with LiveData. And the logic is basically the same as what you'd have in a Presenter. The only difference is how you expose that data to the View (direct method calls vs. observable things).
Communication between various MVVM components and data binding can be painful
Code reusability of views and view model is difficult.
Managing view models and their state in nested views and complex UI’s is difficult
MVVM architecture, “one to many” relationships between View & View Model.
MVVM facilitates a separation of development of the graphical user interface with the help of mark-up language or GUI code
In MVVM, the view is the entry point to the Application.
MVP vs MVVM
With MVVM, only the View has a reference to the ViewModel.
The ViewModel knows nothing about the View - it simply exposes some observable thing (like LiveData) that the View subscribes to.
Is simpler because the dependencies are easier to manage. It also theoretically makes testing the ViewModel easier because you don't need to mock a View. As far as testing is concerned, we can run ViewModel tests on a local JVM (as opposed to an instrumented test on an real Android device), even with LiveData. And the logic is basically the same as what you'd have in a Presenter. The only difference is how you expose that data to the View (direct method calls vs. observable things).
View
The view is the visual element that gets displayed. All the UI components on an app screen are views. The view contains only the UI logic, such as data rendering, navigation, etc.
A part of our app which handles what the user sees and touches on the screen. In other words, a View does all the things an Activity or a Fragment can do.
Views handle only the immediate interaction with the user i.e. we simply don’t put any business logic like communicating with a database inside our Activities or Fragments.
These can only display stuff on the screen (which we get from ViewModel), do Android specific operations and dispatch user interaction events (clicks etc.) to their respective ViewModel.
View Model
A ViewModel is like a bridge between a View and business logic. It provides data for the view by getting it from the repository.
View Models receive UI events and perform business logic and provides the output to be displayed on the UI.
This is the component that is responsible for handling the business logic driving the view.
But it internally does not modify the UI, neither it has any reference to the view.
It doesn’t have any clue about which Views are using it
The trick here is to make the appropriate data in the ViewModel as observable.
By doing this, we get rid of the need to directly update the View from the ViewModel when data changes.
A View will have a reference to its ViewModel, so that it can simply observe some data which the ViewModel exposes.
Whenever the data changes, all of the Views which are observing it will be notified about this change through LiveData which is a handy lifecycle aware library for creating observables.
One of its advantages is that it automatically doesn’t notify the observer if its activity or fragment is already destroyed, leaving you free from managing the lifecycle yourself.
Model
Model is where we put all the business logic specific code.
Model is basically the data model or entities that your application has.
They are simply classes with simple associated properties. In general practice,
they simply hold the data that has been mapped from your raw data structure which might come
from your API’s or other sources such as SQLite files, etc.
Technically there is an intermediate step between the ViewModel and the Model in the form of a Repository
Repository
Acts as a single source of truth for all the data
These operate on our app’s data and fetch it from the local database or from the network.
Repository has a special role of being a mediator between local storage and the server.
This is where you check whether the remote data should be cached locally and so on.
Repository is also the single source of truth for ViewModels.
In other words, when ViewModel wants some data, it gets it from the Repository.
Connectedness of MVVM components
Not only that the View observes data in the ViewModel but also the ViewModel observes data in the Repository which in turn observes data coming from the local database and remote data source.
When traversing down the hierarchy, the upper class has a direct reference to its child. On the other hand, the child doesn’t have a reference to its parent.
Children only expose some data by allowing it to be observed through LiveData.
Shared View Model
Using SharedViewModel, we can communicate between fragments. If we consider two fragments, both the fragments can access the ViewModel through their activity.
One fragment updates the data within the ViewModel which is shared between both the fragments and another fragment observes the changes on that data.
class SharedViewModel : ViewModel() {
val message = MutableLiveData<String>()
fun sendMessage(text: String) {
message.value = text
}
}
Now, we are going to create two Fragments:
MessageReceiverFragment
: This Fragment is going to receive the message which will be sent by MessageSenderFragment. It will have a TextView which will show the received message.MessageSenderFragment
: This Fragment is going to send the message which will be received by MessageReceiverFragment. It will have a Button to send the message.
class MessageReceiverFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_receiver, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val model = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
model.message.observe(viewLifecycleOwner, Observer {
textViewReceiver.text = it
})
}
}
fragment_receiver layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/textViewReceiver"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Your Message"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MessageSenderFragment class:
class MessageSenderFragment : Fragment() {
lateinit var model: SharedViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_sender, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
button.setOnClickListener { model.sendMessage("MindOrks") }
}
}
fragment_sender layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Your Message"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/receiverFragment"
android:name="packagename.MessageReceiverFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/senderFragment"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<fragment
android:id="@+id/senderFragment"
android:name="packagename.MessageSenderFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/receiverFragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.class
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
The working:
- Here, we have created an activity that consists of two fragments. The same activity is the host for both the fragment.
- In both the fragment, we have created the object of SharedViewModel which is the same object as we are using the same single activity as an owner. This is the reason it is shared. Notice that we have used the
requireActivity()
.ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
- In the MessageSenderFragment, we are sending the message on button click, which sets the value of
message - LiveData
in the SharedViewModel. - Then, in the MessageReceiverFragment, we are observing the change in the
message - LiveData
, and whenever the message is coming, we are updating the data in textView.
Comments
Post a Comment