October 18, 2016 / Reading time: 8m

Android MVP without RxJava or Dagger

android

mvp

opensource

How to make software architecture work for you on Android


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.
JoaquimLey/avenging
MVP pattern example on Android: no Dagger or RxJava example - JoaquimLey/avenging
Avenging - MVP project - Apps on Google Play
BLOG POST: https://blog.joaquimley.com/avenging-android-mvp-23461aebe9b5#.d03ea0lv4 GITHUB: https://github.com/JoaquimLey/avenging Uses Marvel Comics API as a service which contains all the information about Marvel’s library. All Data provided by Marvel. © 2016 MARVEL What is MVP? ModelViewPrese…

With this in mind, I set out to build the mobile and wear versions of the same app. Since it's 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 is shared with only a LeakCanary installation, both :mobile and :wear apps 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

    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

    public abstract class BasePresenter<V> {
    
        protected V mView;
    
        public final void attachView(@NonNull V view) {
            mView = view;
        }
    
        public final void detachView() {
            mView = null;
        }
    
        /**
         * Check if the view is attached.
         * This checking is only necessary when 
         * returning from an asynchronous call
         */
        protected final boolean isViewAttached() {
            return mView != null;
        }
    }

    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

    public interface ListContract {
    
        interface ViewActions {
            void onInitialListRequested();
    
            void onListEndReached(Integer offset, Integer limit, String searchQuery);
    
            void onCharacterSearched(String searchQuery);
        }
    
        interface ListView extends RemoteView 
            void showCharacters(List<CharacterMarvel> characterList);
    
            void showSearchedCharacters(List<CharacterMarvel> characterList);
        }
    }
    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:

    mView.hideProgress();
    List<CharacterMarvel> responseResults = response.getData().getResults();
    if (responseResults.isEmpty()) {
        mView.showEmpty();
        return;
    }
    
    if (TextUtils.isEmpty(searchQuery)) {
        mView.showCharacters(responseResults);
    } else {
        mView.showSearchedCharacters(responseResults);
    }
    

    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.

    @Override
    public void hideProgress() {
        mSwipeRefreshLayout.setRefreshing(false);
        mContentLoadingProgress.setVisibility(View.GONE);
        mListCharacterAdapter.removeLoadingView();
    }

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

    @Override
    public void showCharacters(List<CharacterMarvel> characterList) {
        // If we're changing between the normal or search layouts
        if (mListCharacterAdapter.getViewType() != ListAdapter.VIEW_TYPE_GALLERY) {
            mListCharacterAdapter.removeAll();
            mListCharacterAdapter.setViewType(ListAdapter.VIEW_TYPE_GALLERY);
        }
        
        if (!mSwipeRefreshLayout.isActivated()) {
            mSwipeRefreshLayout.setEnabled(true);
        }
        // Adding items to the list.
        mListCharacterAdapter.addItems(characterList);
    }
    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.

    Share