Android MVP without RxJava or Dagger

How to make software architecture work for you on Android

This article was initially published on my website.

TL;DR: Android app with a MVP approach without RxJava or Dagger, find the source code on Github, and the sample on PlayStore.

First, what is MVP (ModelViewPresenter)?

  • View is a layer that displays data and reacts to user actions. On Android, this could be an Activity, a Fragment, an android.view.View or a Dialog.
  • Model is a data access layer such as database API or remote server API.
  • Presenter is a layer that provides the View with data from Model, also handles background tasks.

Most of the modern Android applications just use View-Model architecture. Programmers are involved in a fight with View complexities instead of solving business tasks. Using only Model-View in your application you usually end up with tightly coupled code, which translates into harder scalability. With the MVP approach it easier to understand the role of each element. You didn’t become a developer to be wasting time on boring code maintenance, but to build awesome stuff!

The story 🤓

After reading about android architectures (before this existed), especially MVP decided to give it a try, having no prior experience, I sought out after good quality technical articles to better translate the theory, into code.

Of all these articles I read, there was one that caught my attention the most, that’s Bourbon by Joe Birch. After reading the article and the code multiple times, I started getting the sense how advantageous following this pattern was, decoupling the View and the Domain layers kept the most common problems that come with badly architected software away. So that’s how the idea of writing the Avenging came to be, theory won’t get you anywhere anyway, it was time to open up Android Studio.

If you’re here, chances are you’ve already stumbled upon it, therefore I wrote this app similar to Joe’s, making it easier to understand each element without RxJava or Dagger (although the code may be similar, I’ll refrain from referencing the article too much, the idea is not having the reader jumping between the two articles).

Avenging

An Android app which uses Marvel Comics API as a service. This service contains all the information about Marvel’s vast library. Since this would be proof of concept /example the only features are:

  • Display a list of Characters
  • Search Characters from the list
  • Show a second “details screen” with some extra info.

With this in mind, I set out to build the mobile and the wear version of the same app. Since its the same app but in different platforms, makes sense to have reusable code.

Library module

The solution was creating a library module (with as minimal Android’s framework presence as possible). We’ll call this library “:core” since it remains in isolation, there is no knowledge to where the data is being displayed, be it on mobile or wear. This sort of thinking helps to build it up as context-free and reusable as possible.

:core knows nothing

The package structure for the library is one package per feature/context:

Data

  • model POJOs annotated to parse API responses (using jackson-converter, there are other options).
  • network Includes both the Service (endpoint mapping for retrofit) and the ServiceFactory.
  • DataManager responsible for providing data (Remote or Local).

Ui

  • base These are the abstract implementations, nothing fancy here, most of the following classes will implement one of these.
  • character Contains the Presenter and the contract between the View and the presenter on character detail related logic.
  • list Similar to Character, the logic required to display the Characters list.

AvengingApplication This instance has only LeakCanary installation, and both :mobile and :wear apps will use.

:core on Github

Having a package per feature really (I mean really!) helps your life as a developer. Working on projects with huge codebases, generic packages like “fragments”, “adapters”, can become a nightmare! You’ll find yourself (more often than not) running all around just to find that specific class that you can’t recall the name 😤. This structure just downright simplifies everything.

Interaction between different “layers”

To keep coupling to a minimum, interaction between the domain layer and the view layer goes through a pre-defined contract, the View’s sole responsibility is to inform the Presenter of events (e.g. user clicks some button), and displaying results. Presenters always extend a Contract.View and implement Contract.ViewActions, the View implements the Contract.FooView and has a Presenter (by composition). For example, the CharacterPresenter and CharacterFragment both follow this rule:

public class CharacterPresenter extends BasePresenter<CharacterContract.CharacterView> implements CharacterContract.ViewActions {
...
}
public class CharacterFragment extends Fragment implements CharacterContract.CharacterView,
ComicAdapter.InteractionListener {
...
private CharacterPresenter mCharacterPresenter;
...
}

A contract is a wrapper, encapsulating two other interfaces, the ViewActions and the FooView.

It’s important to break dependencies apart with interfaces, besides the advantageous decoupling, it’s especially useful for unit tests (mocking).

Each layer only cares about its own well-defined responsibilities

  • DataManager (Model) — Doesn’t know about the View or the Presenter;
  • View — Doesn’t know about DataManager or the internals of the Presenter (interacts only via a Contract);
  • Presenter — Doesn’t know about the View or the Database/API;
From purple (top) to green (bottom)

You’ll find some similarities to the MVC pattern, where our Presenter acts much like a Controller.

“It’s all talk until the code runs”

Although I’ll briefly show some of the code, the idea here is to focus on the MVP pattern and not the actual LoC, that being said, even if you are a beginner I want you to be able to understand the project, don’t expect a super technical analysis, at best, it should clear some of the confusion you might have while reading until this point.

BasePresenter

This is the basic implementation for the Presenter. The mView field represents an “entity” (e.g. Activity, Fragment, etc.) that implements the Contract.View.

The basic implementation class has the entity’s lifecycle handling. You can attach and detach the view, in essence, represents the Presenter’s “abstract-view-reference”, the way of knowing if this entity is still available to receive the results, for example:

  • The user performs an action in a certain Activity, which fires an event to the Presenter, this event’s implementation requires the Presenter to make some network request to provide the user information but, by the time the results are received and processed, the user may have exited the Activity, and it gets destroyed, in another words so there is no one to receive this data. See the Avenging core flow diagram above.

How does it work?

Looking into “Character List” feature.

The ListActivity’s only responsibility is to add the ListFragment and display Marvel’s copyright notice in a Snackbar (maybe it makes sense to delegate this logic to the ListPresenter, give me for your opinion in the comments 👍).

ListFragment

This fragment should display a list of characters, with a picture and name, characters search option, and finally, when the user clicks on a list item it should display a details screen.

Having the requirements defined we can now create a Contract between the Presenter and this View (NOTE: The contracts and presenters are the same for both the mobile and wear versions, hence present on the :core library).

ListContract

Encapsulates all the view actions and expected results.

As said before the ListPresenter implements the view actions and calls its internal methods as a reaction, this is part of the MVP’s “magic”, the View is completely unaware of the Business Logic, just waits for data back from the Presenter and display this data to the user. The Presenter is also responsible for updating the View (states) during the whole process, this goes along with the unwariness (that’s a word? 🤔) of the view of what’s happening:

The above snippet show how the Presenter is interacting with the view, in this case, the ListFragment, when it should hide its progress state, and depending on the case show the “empty state”, or the new data:

mView.showCharacters(responseResults)

On the other hand, the Presenter is completely unaware of the View’s behavior/implementation to this interactions.

The hideProgress() implementation removes its loading state/views.

hide progress bars etc.

showCharacters() adds the provided results to the fragment’s list.

The new items will be added to the fragment’s list (line.13).

Although I’ve gone through from the Presenter to Model and only then the View, I recommend you to start developing the other way around. It’s easier to understand and structure your code once you have a notion of what the view should display and possible interactions, should then be clear what the Presenter (and the DataManager) should react (interactions) and provide (data). You can always check the open-source code on Github.

With this implementation , becomes easy to build the :mobile and the :wear apps, all we need to do is provide the user interface 😎.

Some final thoughts on MVP

  • View classes stay neat and classy.
  • Writing tests is a breeze.
  • Coupling remains in the sweet spot of functionality vs complexity.
  • If the requirements change usually is quite simple to pinpoint exactly what you need to change in your code with minimal implications.
Hello! My name is Joaquim Ley, I’m currently based in Lisbon, Portugal.

If you liked this article hit that 💚, if you know someone who might extract value please share, I’ll be looking for your opinion and suggestions in the comments, feedback is always welcome.

If you would like to see more content from me, 👊 the follow button!

I create bugs on Android for a living. Besides coding and opensource, I ❤ photography & filmmaking. www.joaquimley.com

The (retired) Pub(lication) for Android & Tech, focused on Development