Android MVP, Retrofit & Rx

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/repos to 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