Effectively using Retrofit, Otto and OkHttp to communicate with Loopback.io

This story tells you how to use these awesome open source libraries for Android to effectively and easily communicate with the REST API built using Loopback.io or any other REST API development framework.

There are lots of great tutorials on this subject already, but I found I believe it might be worth sharing my experience on how I handled networking in my application.

At first, these libraries might be overwhelming, at least for beginners working on a network based Android app. But with time, these things kind of grow on you and you get comfortable playing around with it.

I had one such experience building an app using these gems of tools for networking effectively.

A typical network communication to get data from REST API developed using Loopback.io and configured using Retrofit

This image is my perception of how these pieces of code communicate with each other to produce real magic. In other words, this depicts behind the scenes of how a typical network request is propagated from an Activity or Fragment to the API and how data is retrieved.

For sake of example, lets say the Entity(Model) we are dealing with is called Observation and is stored on a server accessed via REST. Lets break down the above diagram into separate modules

A request from Fragment to get Observation

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get Observation from server
BusProvider.bus().post(new
ObservationEvent.OnLoadingInitialized(observation.getId(),
ApiRequestHandler.GET));
}

Using event bus, we are decoupling the network handling logic and passing on the control to the RequestHandler which will be responsible for making calls to the REST API using Retrofit.

RetrofitClient

/**
* Retrofit Service Generator class which initializes the calling Service interface passed as an input param. Depending on the
* createService method called, this class will return the Service with or without token.
*/
public class RetrofitApiClient {
// main client
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
// Retrofit
private static Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(ApiResources.BASE_API_URL)
.addConverterFactory(GsonConverterFactory.create());
/**
* Generates the Retrofit Service interface for the type of Service class passed as an input param.
*
*
@param serviceClass
* @param <S>
* @return
*/
public static <S> S createService(Class<S> serviceClass) {
return createService(serviceClass, null);
}

/**
* Generates the Service to to add OAuth 2.0 Access token as the Header to the HTTP call made by Retrofit. The Header is
* added using an OkHttpClient Http Client and it contains the Interceptor to add the Header for incoming and outgoing
* requests.
*
*
@param serviceClass - The Retrofit Service Interface class.
*
@param authToken - Access token retrieved after Successful login by the Account.
*
@param <S>
* @return
*/
public static <S> S createService(Class<S> serviceClass, final String authToken) {
// IMP: always clear the interceptors.
if (authToken != null && !authToken.equals("")) {
httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();

// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header(AUTHORIZATION, authToken)
.method(original.method(), original.body());

Request request = requestBuilder.build();
return chain.proceed(request);
}
});
}

// build the Retrofit instance with the Token Authorization OkHttpClient.
Retrofit retrofit = builder.client(httpClient.build()).build();

// return the ServiceClass passed.
return retrofit.create(serviceClass);
}
}

ObservationEvent

Custom Event type for dispatching events related to Observation model

/**
* Handles all the {
@link Observation} type of events such as loading list, creating new Observation, Deleting observation etc.
* The ObservationEvent is used to post on EventBus regarding any events related to Observation
*/
public class ObservationEvent extends BaseNetworkEvent {
public static final OnLoadingError FAILED = new OnLoadingError(UNHANDLED_MSG, UNHANDLED_CODE);
/**
* Event Handler When the Loading for the Observation Events is initialized and Request is initiated.
*/
public static class OnLoadingInitialized extends OnStart<Observation, String> {

public int apiRequestMethod;
public String activityId;
/**
* Constructor overloading for HTTP GET, DELETE calls.
*
*
@param observationId
* @param apiRequestMethod
*/
public OnLoadingInitialized(String observationId, int apiRequestMethod) {
super(observationId);
this.apiRequestMethod = apiRequestMethod;
}
}
/**
* Event Handler when the Observation Events are successfully executed and Response is generated.
*/
public static class OnLoaded extends OnDone<Observation> {
public OnLoaded(Observation entity) {
super(entity);
}
}
/**
* Event Handler When loading of Observation Events throws errors.
*/
public static class OnLoadingError extends OnFailed {
public OnLoadingError(String errorMessage, int code) {
super(errorMessage, code);
}
}

The reason for building custom event is to enable events for specific type to be published and subscribed. So lets say when we want to publish a POST event to post an Observation, we can simple pass POST in the apiRequestMethod param. The event type will still be the same OnLoadingInitialized but due to the difference in HTTP method, the RequestHandler will call POST.

ObservationRequestHandler

public class ObservationRequestHandler extends ApiRequestHandler {

private ObservationApiService mApiService;
/**
* Constructor overloading to initialize the Bus to be used for this Request Handling.
*
*
@param bus
*/
public ObservationRequestHandler(Bus bus, Context mContext) {
super(bus, mContext);
mApiService = RetrofitApiClient.createService
(ObservationApiService.class, ACCESS_TOKEN);
}
/**
* Subscribes to the event when {
@link Observation} is Loaded.
* Receives the Observation object and make
* network calls to Retrofit depending on the type of request made.
*/
@Subscribe
public void onInitializeObservationEvent(ObservationEvent.OnLoadingInitialized onLoadingInitialized) {
// Here, we can use a switch-case for the apiRequestMethod to 
// separate calling the HTTP methods
// The ObservationEvent will hold the Resource id, in this case 
// ObservationId
Call<Observation> observationCall =  mApiService.get(onLoadingInitialized.getResourceId());
// makes the Calls to network.
observationCall.enqueue(new Callback<Observation>() {
@Override
public void onResponse(Call<Observation> call, Response<Observation> response) {
if (response.isSuccess()) {
// Observation response received. Post on Bus
mBus.post(new ObservationEvent.OnLoaded (response.body()
));
} else {
int statusCode = response.code();
ResponseBody errorBody = response.errorBody();
try {
mErrorResponse = mGson.fromJson(errorBody.string(), ErrorResponse.class);
mBus.post(new ObservationEvent.OnLoadingError(mErrorResponse.getApiError().getMessage(), statusCode));
} catch (IOException e) {
mBus.post(ObservationEvent.FAILED);
}
}
}

@Override
public void onFailure(Call<Observation> call, Throwable t) {
if (t != null && t.getMessage() != null) {
mBus.post(new ObservationEvent.OnLoadingError(t.getMessage(), -1));
} else {
mBus.post(ObservationEvent.FAILED);
}
}
});
}

Finally, when the response is received, it is posted back on EventBus and is subscribed in the same thread as posted

/**
* Subscribes to the event of success in loading of all the {
@link Observation} from server.
*
*
@param onLoaded
*/
@Subscribe
public void onObservationLoadSuccess(ObservationEvent.OnLoaded onLoaded) {
    Observation mObservation = onLoaded.getResponse();
// do something with Observation
}
/**
* Subscribes to the failure event of getting all {
@link Observation}
* from server.
*
*
@param onLoadingError
*/
@Subscribe
public void onObservationLoadFailure(ObservationEvent.OnLoadingError onLoadingError) {
Toast.makeText(mContext, onLoadingError.getErrorMessage(), Toast.LENGTH_LONG).show();
}

As seen , there is a lot to configure in order to use Event bus with Retrofit and OkHttp but as it seems, once the Custom Event types and RequestHandlers are configured, the same implementation can be used for making any sort of network calls; GET, POST, PUT, DELETE etc. Only the method type has to be changed in the Event constructor and you are good to go. Also, this mechanism provides efficient Error handling, Debugging and readability.

The same approach can be more simplified by using Dagger 2.

Please share your comments and feedback if you have any questions.

Show your support

Clapping shows how much you appreciated Pavitra Kansara’s story.