Get your MVP right with Mosby

James Shvarts
ProAndroidDev
Published in
6 min readDec 18, 2017

--

Credit: https://unsplash.com/@franciscomoreno

Model-View-Presenter (MVP) pattern is great but everyone seems to implement it differently. It requires that you make many decisions including: Where do you bind/unbind the Presenter? Do you retain your Presenter on device rotation? Should your Presenter manage state and be lifecycle-aware? Implementing MVP correctly is not a trivial task and requires solid understanding of Android lifecycle.

Wouldn’t it be great if there was a library that helps you implement MVP correctly (and provide additional features such as view state management) so you can concentrate on your app’s business requirements and design?

Meet Mosby

Mosby is an open-source Model-View-Presenter / Model-View-Intent library for modern Android apps maintained by Hannes Dorfmann who has done an amazing job documenting the library on his blog. The library is actively maintained with 4.1K stars on Github and growing.

The name Mosby comes from Ted Mosby, the architect of the tv series How I Met Your Mother. Excellent choice!

It’s great to see that Hannes is also a fan of the popular Conductor library so he created Mosby plugin for Conductor. When combined together, the two libraries allow you to build flexible single Activity Fragment-free apps. 👍

The goal of this article is to help you get started implementing MVP architecture with Mosby and Conductor by showing practical bite-size examples. To do so, I created several Mosby MVP sample apps (including one that uses Dagger). Feel free to dive into the source code directly at https://github.com/jshvarts/MosbyMVP.

Views in Mosby

Traditionally, the View layer of an MVP app would be any of the following:

  1. Activity
  2. Fragment
  3. ViewGroup such as FrameLayout
  4. Controller (if you use Conductor)

With Mosby, your most basic View becomes one of the following:

  1. Activity that extends MvpActivity or implements ActivityMvpDelegate [sample code]
  2. Fragment that extends MvpFragment or implements FragmentMvpDelegate
  3. ViewGroup that implements ViewGroupMvpDelegate or ViewGroupDelegateCallback
  4. Controller that extends MvpController or implements MvpConductorDelegateCallback [sample code]

Handling view state

Mosby addresses one of the biggest challenges in Android development: retaining view data during device rotation or other configuration changes. Fragments can achieve this by using Fragment#setRetainInstance(true). Activities can do this by explicitly defining an Object which is going to be saved in Activity#onRetainNonConfigurationInstance(), not via onSaveInstanceState().

To simplify the above, Mosby introduces ViewState interface that you can implement to save and restore view data on configuration changes. The rationale is there is no need to be loading the data again (no need for the View to contact the Presenter again) after screen rotation. And because the ViewState is managed solely by the View itself, the ViewState effectively lets your Presenter survive rotation.

Surviving process death

What about view state surviving process kills, not just rotation or other configuration changes? RestorableViewState lets you serialize and deserialize your data using a Bundle instead of keeping the view state in memory.

Keep in mind that Android limits a Bundle size to about 1MB. Also, beware that restoring state from Bundle after process death may result in poor user experience due to outdated data being shown.

To use or not to use ViewState?

When architecting your app, ask yourself if view state should be saved and restored. Usually, the answer is ‘yes’ if your app needs to support both screen orientations. Then ask yourself if you need your ViewState to survive process kills and then pick appropriate type of ViewState to implement.

The decision tree above is a good example why I like Mosby — it’s flexible and encourages you to think about your app’s needs upfront: Need to be able to restore the view state? Use Mosby APIs that support ViewState as described below.

To implement ViewState support, the View becomes one of the following:

  1. Activity that extends MvpViewStateActivity or implements MvpViewStateDelegateCallback [sample code]
  2. Fragment that extends MvpViewStateFragment or implements MvpViewStateDelegateCallback
  3. ViewGroup that implements ViewGroupMvpDelegate or ViewGroupMvpViewStateDelegateCallback
  4. Controller that extends MvpViewStateController or implements MvpViewStateConductorDelegateCallback [sample code]

Loading-Content-Error flow made easy

How many times did you have to implement a screen with a loading indicator (such as ProgressBar) followed by displaying either content or an error? Mosby offers LCE (Loading-Content-Error) functionality virtually out-of-the-box. All you have to do is to stick to the following naming convention for your Views:

  • @+id/loadingView for loading indicator view
  • @+id/contentView for content view
  • @+id/errorView for error view

For LCE support, the View becomes one of the following:

  1. Activity that extends MvpLceActivity or extend MvpActivity and implement MvpLceView
  2. Fragment that extends MvpLceFragment or extend MvpFragment and implement MvpLceView
  3. Controller that extends MvpLceController or extend MvpController and implement MvpLceView

For LCE + ViewState support, the View becomes one of the following:

  1. Activity that extends MvpLceViewStateActivity or extend MvpLceActivity and implement MvpLceView and MvpViewStateDelegateCallback [sample code]
  2. Fragment that extends MvpLceViewStateFragment or extend MvpLceFragment and implement both MvpLceView and MvpViewStateDelegateCallback
  3. Controller that extends MvpViewStateController or extend MvpController and implement MvpViewStateConductorDelegateCallback [sample code]

Bonus: LCE + ViewState examples above include a sample of pull-to-refresh functionality that has become extremely popular today.

What about the Presenter?

All examples above use Mosby’s MvpBasePresenter which deals with attaching and detaching the View for you. Alternatively, you can implement MvpPresenter interface yourself.

Prior to invoking methods on the View from your Presenter, remember to check if the View is attached. Written in Kotlin, it would look something like:

{ ifViewAttached {view -> 
view.setData(it)
view.showContent() } }

MvpNullObjectBasePresenter

Your Presenter can extend MvpNullObjectBasePresenter to avoid checks to see if the View is attached. However, this could result in performance degradation as this type of Presenter uses reflection internally.

Note that this requires Gradle dependency com.hannesdorfmann.mosby3:mvp-nullobject-presenter.

MvpQueuingBasePresenter

Sometimes, we need to retry making calls on the View by waiting until the View is re-attached again. In these cases, use MvpQueuingBasePresenter to guarantee that the calls to the View will be queued up and replayed if and when the View gets attached again.

Note that this requires Gradle dependency com.hannesdorfmann.mosby3:mvp-queueing-presenter.

Dagger app with Mosby and Conductor

Here is a minimal Clean Architecture sample app implemented with Conductor and Mosby plugin. It uses Dagger 2 with custom scopes for dependency injection (more on custom scopes below).

The app simply retrieves a list of Github repositories for a given user and lets you get additional details about each repository. As you may have guessed, it uses industry-standard Retrofit library to load Github data. RxJava2 is used to communicate between Presentation, Domain and Data layers.

Personally, I find Mosby API very intuitive and logical. For instance, I wanted to create a Base Presenter and Base View Controller to be shared across my Presenters and View Controllers (Views) in the app. With Mosby, it’s simple in a familiar modular MVP way.

Here is my custom BasePresenter:

And here is a Presenter that extends my BasePresenter:

Here my custom BaseViewController:

And here is a View Controller that extends my BaseViewController:

Injecting the Presenter

The best place to inject your Presenter into View Controller is inside ViewController#createPresenter(). This guarantees that it is injected only once and not on every device rotation. For instance:

Custom scopes

Dagger dependencies defined for each feature have custom @PerScreen scope. This guarantees that when the host View Controller is destroyed, its dependencies will be gone too. For instance, when the Repo Detail screen is closed (RepoDetailViewController is destroyed), the garbage collector will collect the RepoDetailComponent responsible for providing RepoDetailPresenter and RepoDetailUseCase dependencies.

Conclusion

I like that Mosby is distributed in a modular way. You use only the dependencies that you need depending on your app requirements. In addition to controlling the footprint of your APK, it also forces you to think about your app’s needs upfront.

If I had to do an MVP app from scratch, Mosby and Conductor would be my ideal goto libraries. This combination helps you with:

  1. implementing your MVP correctly
  2. concentrate on your business logic and UI and let the library handle the MVP internals
  3. they are flexible and extensible through heavy use of Generics and interfaces
  4. they are well thought out and documented
  5. they let you get away (and save you the headache) from Fragments
  6. Found a bug? Report it or submit a PR! The maintainers are very responsive!

Further Reading

  1. Source code for this article: https://github.com/jshvarts/MosbyMVP
  2. Mosby library and Mosby plugin for Conductor by Hannes Dorfmann
  3. Conductor library by Eric Kuck
  4. Creating Clean Architecture Multi-Project MVP App — my article covering how to build MVP apps with Conductor.
  5. https://github.com/Zhuinden/simple-stack — an alternative library developed by Gabor Varadi

Visit my Android blog to read about Jetpack Compose and other Android topics

--

--