Like most of the Android world, I've started attempting to implement Model View Presenter. A number of people have asked me for implementation details with what I've been trying, so I wanted to just write something up real quick in order to have something I can share next time.

Disclaimer: I'm no expert. If you want a fool-proof example of MVP, then go look elsewhere. I know Hannes Dorfmann has written some good things and there is even a Google Code Lab with an example. I'm just writing this to get better help. By all means though, if you can give me some suggestions, then please do.

My Example

Without going too in-depth, the reason to use MVP is to take all programming logic that is not manipulating a view (visibility, animation, etc...) or handling a lifecycle event out of the Activity, Fragment or ViewGroup, so that you can more easily perform unit testing on business logic.

View

I see the view of MVP as the combination of the thing that controls the Android View (Activity, Fragment or ViewGroup) and the View itself. For example, if I had a MainActivity which inflated the View's defined in activity_main.xml, that would be my view.

I have each of my MVP views implement an interface. An example interface in our MainActivity example might be, MainActivityView and it might contain a number of method definitions to control its display.

public interface MainActivityView {

    void showLoading();
    void showContent();
    void showError();
    void setData(List<MyData> data);

}

My MainActivity that implements this interface might look like this

public class MainActivity extends AppCompatActivity implements MainActivityView {

    @Bind(R.id.loading)
    ProgressBar loading;

    @Bind(R.id.error)
    Button error;

    @Bind(R.id.content)
    View content;

    @Inject
    MainActivityPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        ((MyApplication) getApplication()).getObjectGraph().inject(this);
    }

    @Override
    protected void onResume() {
        super.onResume();

        presenter.attachView(this);
    }

    @Override
    protected void onPause() {
        super.onPause();

        presenter.detachView();
    }

    @Override
    public void showLoading() {
        loading.setVisibility(View.VISIBLE);
        error.setVisibility(View.GONE);
        content.setVisibility(View.GONE);
    }

    @Override
    public void showContent() {
        content.setVisibility(View.VISIBLE);
        error.setVisibility(View.GONE);
        loading.setVisibility(View.GONE);
    }

    @Override
    public void showError() {
        error.setVisibility(View.VISIBLE);
        content.setVisibility(View.GONE);
        loading.setVisibility(View.GONE);
    }

    @OnClick(R.id.error)
    public void errorClickedSoReload(View v) {
        presenter.reloadData();
    }

    @Override
    public void setData(List<MyData> data) {
        setupContent();
    }

}
Presenter

As you can see from the snippet of MainActivity that there is a MainActivityPresenter injected into it, and in lifecycle events onPause and onResume the view is detached and attached to the presenter. The presenters responsibility is going to be coordinating events that happen in the view with events that happen in the business logic. In order to understand its responsibility better, I think I first should explain my model.

Model

The model is the business logic of my app. It loads data from the web and a database and keeps track of that data in memory. It also keeps track of the state of the data, if it is loading, if there was an error in loading or if it loaded successfully.

public class MyDataManager {

    private List<MyData> data;

    private BehaviorSubject<SyncState> syncStateSubject;
    private BehaviorSubject<List<MyData>> dataSubject;

    private MyService service;
    private MySqlLiteOpenHelper sql;

    public MyDataManager(MyService service, MySqlLiteOpenHelper sql) {
        ...

        //get from memory first, then database, then network
        loadData();
    }

    ...

    private void loadData() {
        //get data from memory first, then database, then network

        //track status of the data ex: syncStateSubject.onNext(SyncState.SUCCESS);
        //track data ex: dataSubject.onNext(data);
    }

    ...

    public enum SyncState {
        IN_FLIGHT,
        SUCCESS,
        ERROR
    }

}

When I subscribe to the BehaviorSubject (which I expose as an Observable) I get the last thing pushed through it, similar to an event bus. This way when I attach my view to the presenter after a lifecycle event I can request the current state of the business logic and coordinate with my views accordingly.

Back to My Presenter

Here is how I coordinate between my view and model in my presenter

public class MainActivityPresenterImpl implements MainActivityPresenter {

    private MainActivityView view;

    private MyDataManager manager;

    private CompositeSubscription subscriptions;

    public MainActivityPresenterImpl(MyDataManager manager) {
        this.manager = manager;
    }

    @Override
    public void attachView(MainActivityView v) {
        view = v;

        subscriptions = new CompositeSubscription();

        Subscription stateSub = manager.syncStateObservable()
                .subscribe(new Action1<MyDataManager.SyncState>() {
                    @Override
                    public void call(MyDataManager.SyncState syncState) {
                        if (view == null) {

                            return;
                        }

                        if (syncState == MyDataManager.SyncState.SUCCESS) {
                            view.showContent();
                        } else if (syncState == MyDataManager.SyncState.IN_FLIGHT) {
                            view.showLoading();
                        } else if (syncState == MyDataManager.SyncState.ERROR) {
                            view.showError();
                        }
                    }
                });

        Subscription dataSub = manager.getMyDataObservable()
                .subscribe(new Action1<List<MyData>>() {
                    @Override
                    public void call(List<MyData> data) {
                        if (view == null) {
                            return;
                        }

                        view.setData(data);
                    }
                });

        subscriptions.add(stateSub);
        subscriptions.add(dataSub);
    }

    @Override
    public void detachView() {

        if (subscriptions != null && !subscriptions.isUnsubscribed()) {
            subscriptions.unsubscribe();
        }

        view = null;
    }

    @Override
    public void reloadData() {
        manager.forceNetworkRefresh();
    }

}
Conclusion

This is my current thinking. The main thing my solution suffers from so far is that it's not very generic and can't be used to enforce this pattern throughout the whole app. That's ok with me at this point, I just want to see where it goes before I attempt something like that. I also want to avoid subclassing Activity or Fragment.

If you have any suggestions then please let me know.

Thanks.