· 11 min read

MVVM with architecture components: a step by step guideline for MVP lovers

Ok, so now that MVVM is the standard to implement Android Apps since Google released their Guide to App architecture, I think it’s time to provide some easy information to understand the MVVM pattern from the eyes of an MVP user.

So, if you’ve reached here by chance, but don’t know what MVP is or how to use it in Android, I recommend you to first take a look at this article about the topic that I wrote some time ago.

You also have the option to watch the content on YouTube:

https://youtu.be/QNPHQFeXCuA

MVVM vs MVP - Do I need to refactor all my Apps now?

For a long time MVP has seemed to be the most popular presentation pattern to isolate the UI from the business logic, but now there’s a new cool kid in the game.

Many people asked me whether they should run away from MVP, or what to do when they start a new App. These are some random thoughts about it:

  • MVP is not dead. It’s still a perfectly valid pattern that you can keep using if you used to do it before.
  • MVVM, as a pattern, is not necessarily better. The specific implementation Google made makes sense, but there’s a reason why MVP was used before: it just fits pretty well with the Android framework and with minor complexity.
  • Using MVP does not mean that you cannot use the rest of the architecture components. Probably the ViewModel doesn’t make much sense (as it’s the natural replacer to the presenter), but the rest of the components can be used one way or another.
  • You don’t need to refactor your App right away. If you’re happy with MVP, continue with it. In general, it’s better to keep a solid architecture rather than having all the new trends implemented in different screens of the App. Refactors like this one don’t come at no cost.

Differences between MVVM and MVP

Luckily, if you already know about MVP, learning the MVVM is extremely easy! There’s only one slight difference, at least in the way that it’s usually applied to Android:

In MVP, the presenter communicates with the view through an interface. In MVVM, the ViewModel communicates with the view using the Observer pattern.

I know that, if you read the original definition of the MVVM pattern, it won’t match exactly what I said before. But in the particular case of Android, and by excluding data-binding out of the equation, in my opinion, this is the best way to understand how it works.

Migrating from MVP to MVVM without Arch Components

What I’m doing here is to adapt the example I did for MVP (you can take a look at the repository here) to use MVVM. The new repository is here.

I’m excluding Architecture Components for now, so that we absorb the idea first. Then we can get into how the new “framework” that Google made up works specifically, and the points where it makes things easier.

Creating an Observable class

As we are using the Observer pattern, we need a class that can be observed.  This class will hold the observers and a generic type for the value that will be sent to these observers. When the value changes, the observers are notified:

class Observable<T> {

    private var observers = emptyList<(T) -> Unit>()

    fun addObserver(observer: (T) -> Unit) {
        observers += observer
    }

    fun clearObservers() {
        observers = emptyList()
    }

    fun callObservers(newValue: T) {
        observers.forEach { it(newValue) }
    }

}

Using states to represent UI changes

As we now don’t have a way to communicate directly with the view, we can’t tell it what to do. The way I found more flexible is to have models that represent the UI state.

For instance, if we want it to show a progress, we’ll send a Loading state. The way to consume that state is totally up to the view.

For this particular case, I created a class called ScreenState, which accepts a generic type that will represent the specific state that the view needs.

There may be some specific states that apply to all screens, like Loading (you could also think about an Error state), and then a specific one per screen.

The general ScreenState can be modeled by using a sealed class like this:

sealed class ScreenState<out T> {
    object Loading : ScreenState<Nothing>()
    class Render<T>(val renderState: T) : ScreenState<T>()
}

Then the specific states can have any structure we may need. For the login state, an enum is enough:

enum class LoginState {
    Success, WrongUserName, WrongPassword
}

But for the MainState, as we are showing a list of items and a message, the enum wouldn’t give us enough flexibility. So the sealed class is again extremely useful (we’ll see later why):

sealed class MainState {
    class ShowItems(val items: List<String>) : MainState()
    class ShowMessage(val message: String) : MainState()
}

Converting the presenters to ViewModels

The first thing we no longer need is the View interface. You can get rid of it (and the View argument), because we’ll use an observable instead.

To define it:

val stateObservable = Observable<ScreenState<LoginState>>()

Then, when we want to show a progress indicating that a process is running, we just call the observers with the Loading state.

fun validateCredentials(username: String, password: String) {
    stateObservable.callObservers(ScreenState.Loading)
    loginInteractor.login(username, password, this)
}

When the login finishes, we ask to render the success:

override fun onSuccess() {
    stateObservable.callObservers(ScreenState.Render(LoginState.Success))
}

Honestly, this previous state can be modeled in many different ways. If we want to be more explicit, we could say that it can navigate to main screen with a LoginState.NavigateToMain or similar.

But as this depends on many factors depending on the App structure, I’ll leave it like that.

Then, in ViewModel’s onDestroy, we clear the observers so that we don’t leave dead observers around.

fun onDestroy() {
    stateObservable.clearObservers()
}

Using the ViewModel from the Activity

The Activity now cannot act as the ViewModel’s View, so that’s where the observer pattern gets into action.

First, create a property that holds the ViewModel:

private val viewModel = LoginViewModel(LoginInteractor())

Then, in onCreate, you can start observing the state. When the state is updated, it will call the updateUI method:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    viewModel.stateObservable.addObserver(::updateUI)
}

Here, thanks to the sealed classes an enums, and by using the when expression, everything becomes pretty easy. I’m processing the state in two steps: first the general states, and then the particular LoginState.

The first when will show a progress with the Loading state, and will call another function if it has to render the specific state:

private fun updateUI(screenState: ScreenState<LoginState>) {
    when (screenState) {
        ScreenState.Loading -> progress.visibility = View.VISIBLE
        is ScreenState.Render -> processLoginState(screenState.renderState)
    }
}

This second one hides the progress (in case it was visible), navigates to the next activity if the login was successful, or shows an error depending on the error type:

private fun processLoginState(renderState: LoginState) {
    progress.visibility = View.GONE
    when (renderState) {
        LoginState.Success -> startActivity(Intent(this, MainActivity::class.java))
        LoginState.WrongUserName -> username.error = getString(R.string.username_error)
        LoginState.WrongPassword -> password.error = getString(R.string.password_error)
    }
}

When the button is clicked, we call the ViewModel so that it can do its work:

private fun onLoginClicked() {
    viewModel.onLoginClicked(username.text.toString(), password.text.toString())
}

And then, in onDestroy(), we call to destroy the ViewModel (so that it can detach the observers):

override fun onDestroy() {
    viewModel.onDestroy()
    super.onDestroy()
}

Changing the code to use Architecture Components

For now, we have taken a tailor-made solution as a middle step to MVVM, so that you can see the differences easily. So far, there are no many benefits when compared to MVP.

But there are some. The most important one is that you can forget about the activity being destroyed, so you can detach from its lifecycle and do your work anytime. Thanks to the ViewModel and LiveData, you don’t need to worry when the activity recreates or when it’s destroyed.

Here’s how it works: while the activity is recreated, the ViewModel stays alive. It’s just by time  the activity is finished forever, when the onCleared() method from the ViewModel is called.

ViewModel Lifecycle Taken from developers.android.com

As LiveData is also lifecycle aware, it knows when it has to attach and detach from the lifecycle owner, so you don’t need to take care about it.

I don’t want to dive deeper into how Architecture Components work (it’s deeply explained in the developers’ guide, but I can write another article if you need it), so let’s continue with the implementation.

To use Architecture Components in our project, we need to add this dependency:

implementation "android.arch.lifecycle:extensions:1.1.1"

There are several libraries for architecture components and different ways to include them (depending on whether your using AndroidX or not, for instance). So if you have special needs, take a look here.

Architecture Components ViewModel

In order to switch to the ViewModel, you just need to extend the class from the library:

class LoginViewModel(private val loginInteractor: LoginInteractor) : ViewModel()

Remove onDestroy(), as it’s not required anymore. We can move its code to onCleared(). So that way we don’t need to observe in onCreate and stop observing in onDestroy, just when the ViewModel is being cleared.

override fun onCleared() {
    stateObservable.clearObservers()
    super.onCleared()
}

Now, moving back to the activity, create a property for the ViewModel. It needs to be lateinit because it will be assigned in  onCreate:

private lateinit var viewModel : LoginViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    viewModel = ViewModelProviders.of(this)[LoginViewModel::class.java]
}

That’s the ideal case, when the ViewModel receives no arguments. But if we want that the ViewModel receives arguments via constructor, you must declare a Factory. This is the way (a bit convoluted, I know…):

class LoginViewModelFactory(private val loginInteractor: LoginInteractor) :
    ViewModelProvider.NewInstanceFactory() {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return LoginViewModel(loginInteractor) as T
    }
}

And getting the ViewModel changes a bit:

ViewModelProviders.of(
    this,
    LoginViewModelFactory(LoginInteractor())
)[LoginViewModel::class.java]

Replace Observable with LiveData

The LiveData can safely substitute our Observable class. One thing to take into account is that LiveData is immutable by default (you can’t change its value).

This is great, because we want it to be public so that observers can subscribe, but we don’t want to let other parts of the code change the value..

But, on the other hand, the data needs to be mutable, why would we observe it otherwise? So the trick is to use the equivalent to a private field plus a public getter with a more restrictive return type. In the case of Kotlin, it would be a private property plus a  public one:

private val _loginState: MutableLiveData<ScreenState<LoginState>> = MutableLiveData()
val loginState: LiveData<ScreenState<LoginState>>
    get() = _loginState

And we don’t need onCleared() anymore, as LiveData is also lifecycle aware. It will know the right time to stop being observed.

To observe it, the cleanest way is as follows:

viewModel.loginState.observe(::getLifecycle, ::updateUI)

Take a look at my article about function references if you find this line confusing.

The definition of updateUI requires a ScreenState as an argument, so that it fits the return value of the LiveData and I can use it as a function reference:

private fun updateUI(screenState: ScreenState<LoginState>?) {
    ...
}

The MainViewModel doesn’t require onResume() either. Instead, we can override the getter of the property, and run the request the first time the LiveData is observed:

private lateinit var _mainState: MutableLiveData<ScreenState<MainState>>

val mainState: LiveData<ScreenState<MainState>>
    get() {
        if (!::_mainState.isInitialized) {
            _mainState = MutableLiveData()
            _mainState.value = ScreenState.Loading
            findItemsInteractor.findItems(::onItemsLoaded)
        }
        return _mainState
    }

And the code for this activity is very similar to the other one:

viewModel.mainState.observe(::getLifecycle, ::updateUI)

The previous code seems to be a little more complicated, but that’s mainly because of the use of a new framework and until you understand how it works.

There’s certainly some new boilerplate, like the of the ViewModelFactory, the ViewModel lookup, or the two properties required to prevent LiveData edition from outsiders. I simplified some of them in this article by using some Kotlin features, and they may help you feel more comfortable with the code. I didn’t want to add them here for simplicity.

As I mentioned at the beginning, whether you decide to use MVP or MVVM is totally up to you. I don’t think there’s an urge to migrate if your architecture is solved by using MVP, but it’s interesting to have an idea of how MVVM works because you’ll need it sooner or later.

I think we are still in a point where we are trying to find out the better ways to use MVVM with architecture components in our Android Apps, and I’m sure my solution is not perfect. So please, let me know everything that worries you or that you don’t agree with, and I’ll be happy to update the article with your feedback.

Remember that you have access to the complete code on my Github (some stars are always appreciated 😁)

    Share:
    Back to Blog