MVVM is unarguably the best architectural pattern for most Android apps in 2020. The Model-View-ViewModel architecture uses these 3 components to create an architecture that features many strengths over its predecessors, has tons of official support from Google in the form of Architecture Components, and is backed up by a community of developers trying to push the concept to greater strengths.
What do you know about the MVVM architecture though? Perhaps you are a new developer, or an Android veteran trying to regain touch with the ecosystem which is a completely different landscape now than it was 5 years ago.
This post aims to introduce you to MVVM as well as its strengths, history, and basic application. The only prerequisite knowledge you’ll need is how to do simple Android development, preferably in Kotlin as that’s how we do things in 2020.
The 3 Components of MVVM
MVVM stands for Model-View-ViewModel. Each component has a different role.
Model: The Model is the source of data needed in an activity, at least among the MVVM components. A model class might fetch data from a Room database, or from REST APIs. As such, a model class may depend on multiple repository classes or data services. It is sometimes known as the data layer as this is it provides all the data an activity will need.
ViewModel: The ViewModel is the bridge between the model and the view. It calls functions from within the model to get the data it provides, performs any logic on this data if further processing is required, then passes it to the view. The ViewModel however doesn’t know of the view, but rather, the other way around. This is sometimes known as the logic layer as this is where you want to be processing all your data from the model before passing it to the view.
View: The View where the UI is contained, thus it handles any changes made to it. It receives data from the ViewModel and doesn’t know of the Model’s existence. The View can either be an Activity or a Fragment. It’s sometimes known as the UI layer as all UI changes are made here.
The MVVM Structure
The diagram here shows the flow of data going all the way from the Model to the View. There are few things to take from this chart:
- The Model gets the data, passes it into the ViewModel for processing which then passes it to the View for displaying
- Each component only knows of the component above it. The Model doesn’t know of the ViewModel or View. The ViewModel only knows of the Model, and the View only knows of the ViewModel.
I think that’s enough theory for now. Let’s put some of it into practice.
Grab the Dependencies
implementation 'androidx.fragment:fragment-ktx:1.2.4'
Most of the Android Architecture components needed for a standard MVVM implementation are included by default along with AndroidX, but we’ll add this one dependency to make our fragment-viewmodel connections a little bit easier.
Defining the MVVM Components in Code
Our app structure
I like keeping all components related to an activity in its own folder so for the MainActivity, I’m going to put it in a folder named “main”.
Here we have the MainActivity, its Fragment (View), ViewModel, and Model.
MainActivity.kt
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
It is in the activity where we want to attach the fragment, but my activity can very well be set up with the default generated code and nothing else. That’s because the magic happens in the XML code.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:id="@+id/main_fragment" android:layout_height="match_parent" android:name="com.ericdecanini.basicmvvm.main.MainActivityFragment" tools:context=".main.MainActivity" />
What I’m using here in the FragmentContainerView
. It’s a subclass of FrameLayout
that supports attributes for <fragment>
but offers more fragment transaction flexibility. In other words, this allows you to dynamically switch between fragments that use the same ViewModel while also keeping it alive.
Now note the android:name
attribute. This is where I’m defining my fragment class and this will in turn, load the fragment into the activity.
MainActivityFragment.kt
class MainActivityFragment: Fragment() { private val viewModel: MainActivityViewModel by activityViewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_main, container, false) } }
This is the bare minimum you need in an MVVM Fragment. It knows of the ViewModel but has no knowledge of the Model’s existence. By using by activityViewModels()
, we can get a reference to our ViewModel and have its scope tied to our activity.
The XML layout inflated here is what you want to display to the user.
MainActivityViewModel.kt
class MainActivityViewModel: ViewModel() { val model = MainActivityModel() }
The ViewModel class itself is pretty simple in the fact that you pretty much don’t need any boilerplate code for it at all. All that’s happening here is the creation of the model, so the full contents of your ViewModel class can easily be all about the logic you need for your activity and its data.
Note that the ViewModel has no knowledge of the View’s existence.
MainActivityModel.kt
class MainActivityModel { }
It’s an empty class, yes. The Model doesn’t have to be anything special really. There is no Android Architecture model component. This is the class designed to fetch all your data, be it from Retrofit, Room, Firebase, etc. and how you do that is entirely up to you.
Also note that the Model has no knowledge of either the View or the ViewModel.
So now we can run MainActivity, and just like that, it will be running with the MVVM engine. Your fragment is loaded by the activity via xml, which loads the ViewModel, which loads the Model.
It still doesn’t really do anything though, and that’s because we didn’t give it any information outside of initialising the components. We’ll do that, but first, a little more theory.
MVVM View: Activity vs Fragment
Earlier, you saw me use a Fragment for the View but a View can either be an Activity or a Fragment, so what are the advantages and disadvantages of each approach?
In my opinion, it’s always better to use a Fragment. This is because of the ability to have multiple fragments share a common ViewModel. It’s very common for a single activity to have multiple fragments, so setting up your fragments as the View instead of the Activity will make this communication much easier. Otherwise, you’d have to receive the data in the activity, then let that pass data into your fragments, and well yeah, it’s more trouble than you need any day of the week.
Are there any situations I think i’s better to use an Activity? Well, if you’re 100% sure your app will never scale to need multiple fragments in an activity, then sure go ahead. I wouldn’t recommend it though because you can never know when your app might meet new requirements.
I wouldn’t even say it’s much faster to opt for the Activity over Fragment, purely because it’s very little effort to set up a Fragment as a View as you saw above… Just use Fragments.
Passing Data through MVVM Components
We established that the Model is the source of our data as far as the MVVM components go. Purely for the sake of this tutorial, we won’t be using Retrofit, Room, or any other real data source for that matter. Our model here will simply pull from dummy data.
I’m going to set up a scenario where my Main Activity wants to display a randomly picked dog breed as a string, and can update this string on the click of a button. That’s what we’re going to build here.
Our new app structure
I’ve done some minor refactoring here. I moved the main folder into a new folder called activity, and defined a new folder called repository. A common practice in MVVM (and perhaps, even outside of it) is to have repository classes. (Don’t forget to change the reference to your fragment in your activity’s XML code)
A repository can be seen as a source of data classified by a certain entity or purpose. In this case, I created a DogRepository
class which will be responsible for fetching any data related to dogs.
The reason we do this is that multiple activities can request similar pieces of data. A common example is an e-commerce app can have multiple activities that request the same data about the products they sell. We don’t want to tie these data-fetching functions to a single model, otherwise we’d have duplicate functions in many of our models.
DogRepository.kt
class DogRepository { private val breeds = listOf( "German Shepherd", "Bulldog", "Poodle", "Labrador Retriever", "Golden Retriever", "Beagle", "Yorkshire Terrier", "Dachshund", "Chihuahua" ) fun getRandomBreed(): String { return breeds.random() } }
Again, normally you’d have a services that fetch from Retrofit or Room in these repositories, but since we’re not covering that here we’ll simply have a function that returns a random item from a list of dog breeds.
class MainActivityModel { val dogRepository = DogRepository() fun getRandomBreed(): String = dogRepository.getRandomBreed() }
The Model contains an instance of the repository class and includes functions that makes use of that repository. A Model doesn’t need to be tied to using a single repository though. Say you have a DogRepository
as well as a CatRepository
class. It’s perfectly fine to include both of those in a single Model.
class MainActivityViewModel: ViewModel() { val model = MainActivityModel() val dogBreedLiveData = MutableLiveData<String>() init { getRandomDogBreed() } fun getRandomDogBreed() { val dogBreed = model.getRandomBreed() dogBreedLiveData.postValue(dogBreed) } }
LiveData
is the meat of the communication between the ViewModel and the View. As we’ll see later, the view observes these to obtain their value every time they’re updated by either setValue
or postData
.
If you have any mapping to do, you also want to do it here before posting the value.
class MainActivityFragment: Fragment() { private val viewModel: MainActivityViewModel by activityViewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_main, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupClickListeners() observeDogBreed() } private fun setupClickListeners() { dogbreed_button.setOnClickListener { viewModel.getRandomDogBreed() } } private fun observeDogBreed() { viewModel.dogBreedLiveData.observe(viewLifecycleOwner, Observer { breed -> dogbreed_textview.text = breed }) } }
Now our fragment is a little more loaded. Note the observeDogBreed
function.
By observing the LiveData
from the ViewModel, we can obtain its value in real-time whenever new updates are posted to it and call the function we define in its lambda. Here I’m simply applying the string obtained from it directly into my TextView.
The Result
The result is a nice flow of data where each class has its own role to fulfil so we’re following the Separation of Concerns principle nicely here. Our components are also loosely coupled and quite individual, yet we can also reuse any components we’ll need to.
Another benefit to this way of doing things is that since our functions are so modular, writing unit tests for this becomes an absolute breeze and if a test does fail and show us there’s something wrong in our system, we can much more easily pinpoint the cause of the error.
Check Out the Code
If you want to check it out, you can checkout the project from Github in the link below.
https://github.com/ericdecanini/BasicMVVM
If you like this post, please give this a thumbs up, comment on it, share it on Twitter or LinkedIn, I don’t know, just give me something. I’m a small name and I have some big names to compete against in the world of Android blogging.
In any case, happy coding ༼ つ ◕_◕ ༽つ