Blog

We believe there is something unique at every business that will ignite the fuse of innovation.

Designing an Android networking architecture that fits into the Model-View-Presenter (MVP) approach can be challenging and confusing to say the least.  Yet, an even greater challenge is building one that fits into the Android Lifecycle as well.  Sure we could build out our MVP approach using Retrofit, Volley, or even trusty AsyncTasks, but how do we handle device rotation during a networking request? 

The purpose of this blog is to shed some light on a networking architecture that can fit easily into the MVP pattern while also allowing our network requests to live beyond the life of an activity/fragment with very little effort.  This blog assumes a basic understanding of RxJava and Retrofit.  If you need a refresh or an introduction to RxJava, I highly recommend checking out my colleague’s blog Getting Started with RxJava and Android. Be sure to check out the associated demo application

The MVP Approach

Before we proceed, let’s briefly cover what an Android MVP approach might look like and why such a pattern is worthwhile.  

The purpose of the MVP approach is to eliminate the view’s dependency on its data source. This allows each of the three layers to be tested individually and makes our application more robust.  As for the appropriate way to implement the MVP pattern, let’s just say there is no one-way to do so. In fact, a quick Google search could supply you with enough reading material on the topic for weeks, each with their individual variations.  

Below, and in the sample application attached, I describe the most common way to organize the layers within Android and how I usually define each layer’s responsibilities. 

  

The Presenter Layer

The Presenter is a class created solely to be the communicator between the View Layer and Model Layer.  The Presenter hosts methods which are utilized by the View Layer. The Presenter also hosts logic to return formatted data from the Model back to the View for display to the user.  

The Presenter may implement an interface sometimes referred to as an “Interactor” which sets up a method structure or a contract to be used by the View.  The Presenter is responsible for requesting data from the Model layer and handling the response from the associated request.

The View Layer

The View is usually represented by either an activity or fragment depending on the application’s approach.  In the case of my simple application we're using an activity but the same concept can be leveraged with a fragment as well.  

Our View will have a reference to a Presenter, which is typically instantiated in our View.  The primary responsibility of the View is to fire methods in the Presenter class whenever the user interacts with the View.  The View will also host methods that accept formatted data from the Presenter class to be sent to the UI elements.  

The Model Layer

The Model Layer represents the source of the data that we want to display on the View Layer.  In our instance of MVP in the sample application, the Model Layer is the NetworkService class, which works in conjunction with the POJO classes. It is best to set up the Model Layer in such a way that the Presenter does not know whether it is getting the data from the disk, memory, or network.  

The Model Layer with Retrofit 2.0 and RxJava

Now that we have a basic understanding of how to implement a MVP pattern, let's look more specifically at the Model Layer.  As I alluded to before, one of the biggest challenges in designing a networking architecture is designing one that works well with the Android Lifecycle. 

Why is this so difficult? Well, I won’t go into detail here about Android’s activity/fragment Lifecycle but in short when the user rotates their device the entire activity/fragment is dropped and then recreated. For more information check out the Android Developer site.

If you've attempted a standard network implementation (Retrofit, Volley, Asynctasks, or some other approach), you probably noticed that everything works great until the user decides to rotate their device in the middle of a network call. Your request still gets fired but all references back to any listeners/interfaces are dropped.  

Many of us have found band-aids for such an issue, some including either preventing screen rotation during a network call (or all together…. eek!), setting up a series of services and receivers, or even creating a fragment who’s instance is retained to host all our work.  While these all function, they each have their own drawbacks. Not to mention we are taking what was once a simple solution and making it far too complex. 

Retrofit 2.0 and RxJava Request

Before we get too far into how we can fix our lifecycle woes lets first take a look at how to implement RxJava with Retrofit 2.0.  To start, we must first include the following libraries in our build.gradle file.  At the time of this post we're still on beta3 of Retrofit 2.0 but if a newer version is available I would recommend using that.

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta3'
compile 'io.reactivex:rxandroid:1.1.0'

If you've ever used Retrofit before, you're familiar with the basic interface setup where we define our request and any data to be passed with it. For our purposes we will use a simple Get Request with no request body or parameters.  

public interface NetworkAPI {
     @GET("FriendLocations.json")
     Observable<friendresponse> getFriendObservable();
}

Note that rather than having the typical typed Call class defined as the return value for our request method, we will actually be returning an Observable object of type FriendResponse.

Next we need to set up our Retrofit instance in order to create our NetworkApi interface.  For my example I am choosing to use the GsonConverterFactory, but any converter factory of your choice will do.  When using RxJava with Retrofit 2.0 we also need to include the RxJavaCallAdapter.

Retrofit retrofit = new Retrofit.Builder()
     .baseUrl(baseUrl)
     .addConverterFactory(GsonConverterFactory.create())
     .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
     .build();

NetworkAPI networkAPI = retrofit.create(NetworkAPI.class);

With that we are now ready to create our Observable and make our request.

Observable<FriendResponse> friendResponseObservable = networkAPI.getFriendsObservable()
     .subscribeOn(Schedulers.newThread())
     .observeOn(AndroidSchedulers.mainThread());

friendResponseObservable.subscribe(new Observer<FriendResponse>(){
     @Override
     public void onCompleted(){}

     @Override
     public void onError(Throwable e){
          //handle error
     }

     @Override
     public void onNext(FriendResponse response){
          //handle response
     }
});

The code above creates an Observable of type FriendResponse from the NetworkAPI interface.  We then declare that we want our observable to work on a new thread and return its response to the main thread. 

Our request is fired upon calling subscribe().  The subscribe() method returns a Subscription object, which we will use later to prevent leaks. 

But still, the implementation above does not solve our lifecycle issue. Once our device is rotated or the activity/fragment is killed for whatever reason, our Observable is destroyed and we have no reference to the request we made or any corresponding response.  In fact, when you compare it to just a standard Retrofit request, we have actually complicated things.  

Fortunately, this is just the first step to solving our problem.

Lifecycle Safe Request

So how do we solve our lifecycle issue? Well first, let’s create a Singleton class which will be instantiated in our Application class.

public class RxApplication extends Application {

     private NetworkService networkService;

     @Override
     public onCreate() {
          super.onCreate();
          networkService = new NetworkService();
     }

     public NetworkService getNetworkService(){
          return networkService;
     }
}

Next we need to place the Retrofit API interface and Retrofit builder inside of our newly created Singleton.

public class NetworkService {

     private static String baseUrl = "https://dl.dropboxusercontent.com/u/5770756/"
     private NetworkAPI networkAPI;

     public NetworkService() {
          this(baseUrl);
     }

     public NetworkService(String baseUrl) {
          Retrofit retrofit = new Retrofit.Builder()
               .baseUrl(baseUrl)
               .addConverterFactory(GsonConverterFactory.create())
               .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
               .build();

          networkAPI = retrofit.create(NetworkAPI.class);
     }

     public NetworkAPI getAPI() {
          return networkAPI;
     }

     public interface NetworkAPI {
          @GET("FriendLocations.json")
          Observable<FriendResponse> getFriendObservable();
     }
}

Finally, we need to place the logic for our observable creation inside of our singleton.  We need to do so in such a way that we only create an observable object when a cached one is not available, or we explicitly need a new one. We also need to find a way to store any newly created observable for reuse. 

First, let’s create a place to store any observable that we might reuse later.  I accomplish this by creating a LruCache inside of my singleton class. 

private LruCache<Class<?>, Observable<?>> apiObservables = new LruCache<>(10);

Next, let’s create a method that our presenter will call to retrieve an observable.  This observable is returned to the presenter could be a cached observable or a completely new one and it will make no difference to the presenter.

public Observable<?> getPreparedObservable(Observable<?> unPreparedObservable, Class<?> clazz, boolean cacheObservable, boolean useCache){
     Observable<?> preparedObservable = null;

      if(useCache) //this way we don't reset anything in the cache if this is the only instance of us not wanting to use it.
          preparedObservable = apiObservables.get(class);

     if(preparedObservable!=null)
          return preparedObservable;

     //we are here because we have never created this observable before or we didn't want to use the cache...

     preparedObservable = unPreparedObservable.subscribeOn(Schedulers.newThread())
          .observeOn(AndroidSchedulers.mainThread());

     if(cacheObservable){
            preparedObservable = preparedObservable.cache();
            apiObservables.put(clazz, preparedObservable);
     }

     return preparedObservable;
}

The method above accepts any typed observable as well as the corresponding class parameter, which we will use to store our observable in the LruCache

The boolean parameters are used to determine if we want to cache the observable or reuse a cached observable. If you always want to cache and reuse your observable, these are not necessary.  

If a cached observable is not used or desired we proceed with setting up the “unprepared” observable passed into the method. In order to appropriately cache the observable, we need to call the cache() method on it and to add the it to the LruCache.  If we don't call the cache() method on the observable before we add it to the LruCache the instance of the observable will be saved but the response/request will not.  

Our Presenter Layer is now ready to request data from the Model Layer (NetworkService singleton), which will return an observable that already has data, is already retrieving data, or will need to request data.  

With the method below, each time we call subscribe() from any observer we will reuse any response/request that has already been made. This means our onNext() and onComplete() will fire instantaneously if the response has already been returned and our request will only be made once.  

Observable<FriendResponse> friendResponseObservable = (Observable<FriendResponse>)service.getPreparedObservable(service.getAPI().getFriendsObservable(), FriendResponse.class, true, true);

subscription = friendResponseObservable.subscribe(new Observer<FriendResponse>() {
     @Override
     public void onCompleted(){ }

     @Override
     public void onError(Throwable e){
          view.showRxFailure(e);
     }

     @Override
     public void onNext(FriendResponse response) {
          view.showRxResults(response);
     }
});

The Catch

Of course there is a slight catch.  With RxJava, if we're not careful, it's really easy to leak our subscribers. As I mentioned earlier, when you susbcribe to an observable, a subscription object is returned.  This object has two methods, isUnsubscribed() and unsubscribe().  This allows us to release any existing subscription before our activity/fragment is destroyed.  

To do so, from our View (activity/fragment) we need to call the following method on the Presenter Layer during the onPause() or onDestroy().

public void rxUnSubscribe(){
     if(subscription!=null && !subscription.isUnsubscribed())
           subscription.unsubscribe();
}

The method above checks to see if the subscription object exists and if we're currently subscribed to it. If both of those conditions are true we will unsubscribe.  

With that in mind, we also need keep track of when a request has been made so that we re-subscribe to our cached observable after the view is recreated. I do this via the savedInstanceState() in the example application attached.  This then allows our presenter to take care of either continuing to wait for our request to be completed or instantaneously populating our View with the cached response.

Closing

Retrofit 2.0 and RxJava are a great team when working with MVP patterns.  We can finally make a request and not have to worry about when its response is returned while also simplifying the work on our View Layer.  I highly recommend checking out the attached example application and my colleagues post on RxJava for more information. 

About the Author

Clinton Teegarden

Clinton Teegarden is a Senior Software Engineer and Manager at CapTech based in the Washington, DC metro area. Clinton is passionate about bringing the greatest technologies in both Mobile and IoT to his clients using proven architecture and design patterns. Clinton specializes in all things Android and has led teams in delivering products for Fortune 500 clients, servicing millions of users.