Android MVP, Retrofit & Rx

Sep, 14, 2015 • Jan Van Coppenolle

Categories: Android, Mosby, MVP, Retrofit, RxAndroid, RxJava

In this article we’ll discuss the benefits of using Rx over Callbacks in (but not limited to) a Model-View-Presenter application architecture.

It is assumed you are already familiar with Retrofit and the concepts of Android MVP and RxJava.

You can find the example app from which the code examples in this article are taken over here on our GitHub repo.

The Retrofit Part

(Using Retrofit 1.9.0, we will discuss Retrofit 2.0.0 in a future article.)

For the sake of the example we’re using the GitHub api call to https://api.github.com/users/google/reposto get a list of Google’s repos.

	public interface GitHubAPI {
    	String URI = "https://api.github.com";

    	@GET("/users/google/repos")
    	List<GitHubRepo> getReposSync();

    	@GET("/users/google/repos")
    	void getReposAsync(Callback<List<GitHubRepo>> result);

    	@GET("/users/google/repos")
    	Observable<List<GitHubRepo>> getReposRx();
	}

Now let’s go over these methods one-by-one…

1 – Synchronously

List<GitHubRepo> getReposSync();

While there is nothing wrong with using the Retrofit synchronous api on a separate thread, using this directly on the main thread is a no-no…

This blocks the UI until the request execution has finished. From Ice Cream Sandwich onwards, Android’s Strict Mode is going to keep us from being that silly… (and it will throw a NetworkOnMainThreadException).

2 – Asynchronously

void getReposAsync(Callback<List<GitHubRepo>> result);

This is the most commonly used way to perform the request asynchronously. You will handle the result in the success / failure methods of a retrofit.Callback<T> implementation.

3 – Asynchronously with RxJava

Observable<List<GitHubRepo>> getReposRx();

Retrofit supports Rx out-of-the-box. Awesome-sauce…

The MVP Part

Our interfaces RepoPresenter and RepoView are straightforward enough.

The View (in this case an Activity) isn’t aware of how the result is handled. The Presenter is directing the show (pun intended).

There are a few good libraries that facilitate implementing an MVP architecture, and the one we’re using in this example is Mosby.

Presenter implementation options

Before we get to the awesome-sauce, let’s first discuss an implementation in our Presenter to fetch the results asynchronously with the Callback.

By-the-book

	public void loadRepoList() {
		gitHubAPI.getReposAsync(new Callback<List<GitHubRepo>>() {
            @Override
            public void success(List<GitHubRepo> gitHubRepos, Response response) {
                getView().showRepos(gitHubRepos);
            }

            @Override
            public void failure(RetrofitError e) {
                getView().showMessage(e.getMessage());
            }
        });
	}

Great! That works and you go home, but in the morning the statistics tell you there were quite some crashes of you app.

Consider the following scenario: the request is (pick one) taking more time the user can handle / user gets a call / user switches apps and the OS decides to destroy the Activity to free up resources, before the result was processed…

By the time the success or failure methods are invoked, the View has gone and the app will crash (in the background) because of a NullPointerException…

You will actually be warned of this by the compiler if your Presenter extends Mosby’s MVPBasePresenter, as getView() has a @Nullable annotation (uses a weak reference on the view).

Improved

Well… you could solve this by using try / catch statements.

	public void loadRepoList() {
	gitHubAPI.getReposAsync(new Callback<List<GitHubRepo>>() {
            @Override
            public void success(List<GitHubRepo> gitHubRepos, Response response) {
            	try {
                	getView().showRepos(gitHubRepos);
                } catch (Exception e) {
            		e.printStackTrace();
        		}
            }

            @Override
            public void failure(RetrofitError e) {
            	try {
                	getView().showMessage(e.getMessage());
                } catch (Exception e) {
            		e.printStackTrace();
        		}
            }
        });
	}

Or we could check MVPBasePresenter.isViewAttached().

	public void loadRepoList() {
	gitHubAPI.getReposAsync(new Callback<List<GitHubRepo>>() {
            @Override
            public void success(List<GitHubRepo> gitHubRepos, Response response) {
            	if (isViewAttached()) {
                	getView().showRepos(gitHubRepos);
                }
            }

            @Override
            public void failure(RetrofitError e) {
            	if (isViewAttached()) {
                	getView().showMessage(e.getMessage());
                }
            }
        });
	}

Or… we could avoid these checks which makes our code simpler.

Observable Subscribe / Unsubscribe

Using Rx, we have a very clean approach to have our request executed on an IO thread and to subscribe / unsubscribe depending on how interested we are in the result on the main (UI) thread. Rx has built-in mechanisms using a Transformer to achieve this, which can be applied through Observable.compose()

The Presenter implementation below is already making use of some under-the-hood stuff we’ll explain next:

	public void loadRepoList() {
        new RxIOSubscription<List<GitHubRepo>>().add(
                gitHubAPI.getReposRx(),
                new Subscriber<List<GitHubRepo>>() {
                    @Override
                    public void onNext(List<GitHubRepo> gitHubRepos) {
                        getView().showRepos(gitHubRepos);
                    }

                    @Override
                    public void onCompleted() {
                        getView().showMessage("Yihar!");
                    }

                    @Override
                    public void onError(Throwable e) {
                        getView().showMessage(e.getMessage());
                    }
                }
        );
    }

RxIOSubscription.add(...) takes Observable and Subscriber arguments and makes sure a couple of things happen:

  • subscribe on Schedulers.io()
  • observe on AndroidSchedulers.mainThread() using RxAndroid (Have a look at SimpleRxUtil)
  • after scheduling, that observable is subscribed to and added to a CompositeSubscription
  • detachView(...) is called when the Activity is destroyed, which unsubscribes all…

Our code is now simplified again without the risk of nullpointer exceptions.

Conclusion

Retrofit in combination with RxJava and RxAndroid is an awesome stack to use behind your Presenter pattern… this article covers a very specific use-case but the possibilities go far beyond this example.

Notes

Continue reading

View all articles

Getting creative at the KBC innovathon

Mobile banking, a convenience to us all, already rocks with mobile payments being fast, easy and super-accessible. However having an app does not mean you can rest easy. In our fast moving digital world it is key to show off your new designs, announce new features and of course k[...]

Angular 2.0, what to expect?

Some time ago the Angular team announced they’re building on a version 2.0. This version was going to be revolutionary instead of evolutionary. This may sound drastic, but it’s meant in a good way. The idea is to keep the good parts of Angular 1.x and make them more compli[...]