Before we begin
When creating mobile apps you have many decisions to make that will have strong influence on final project’s shape. One of them is choosing the right libraries.
Currently, it is hard to imagine mobile app that doesn’t communicate with network services. Weather apps download weather forecasts from servers, sport & fitness apps store our training data, social apps let us communicate with other users – these are only few examples.
Here at Snowdog, Mobile Team Android uses Retrofit to communicate with APIs. Today I would like to show you a few tips on how to use it to make your code simpler, cleaner and more maintainable.
Basic steps
Adding library to your project
To start using Retrofit you have to add the library to your project by entering in dependencies section of your build.gradle file given entries:
okhttp libraries are strongly recommended, as they allow to bypass many native http client issues. Gson will also be needed for serializing and deserializing between Object and JSON format.
Preparing API interfaces
Next step is consulting with your API documentation and preparing interfaces for Retrofit. Example class below:
If there is something unclear here please refer to official Retrofit documentation.
Creating RestAdapter
It is advised to create single instance of RestAdapter per application (one of the best places to put it in would be your Application class). Make it public static so you can reference it from other parts of your project. You can also introduce public static fields of your API interfaces.
Having added these fields initialize them:
API_URL is a String containing url of api server (e.g. https://api.example.com, https://example.com/api/1.0, etc) without trailing slash. By means of setLogLevel() method you can adjust the amount of data being dumped to the log (RestAdapter.LogLevel).
Sending request
Having done this you can now access your API service easily from any part of your project by simply calling:
This sums up essential parts needed for communication with your api server, which is easy to implement but you can make it even better.
Modifications
Sometimes api requires you to provide authentication via key (tokens anyone?), which you might have to include in url or header. Of course you can do this by modifying your interfaces like this:
It will work fine but there is a better way to do this.
RequestInterceptor
RequestInterceptor is Retrofit’s interface that allows us to make some modifications (add additional data) to the request before it is executed. You can add headers, query params, etc, for all requests that go through RestAdapter instance that your RequestInterceptor is bound to. To bind your interceptor call method setRequestInterceptor() on RestAdapter.Builder() when instantiating RestAdapter:
RequestInterceptor has only one method to implement – intercept(), which allows you to manipulate request by means of RequestFacade object.
There are 3 methods (well, actually 5, but 2 of them only differ slightly from others, see javadoc) to call on RequestFacade:
- addHeader()
- addQueryParam()
- addPathParam()
Their functions are identical to those provided by annotations when declaring API interfaces (@Header, @Query, @Path).
This lets us remove the clutter and code duplication from our project. Useful, isn’t it?
But wait – there is more!
Error handling
Any request can end up returning error that’s why besides success method there is a failure method in Callback. It works as advertised, no objections, but imagine a project where you have many api calls in different places and now you have to handle the same errors all across the project’s code. By such errors I mean e.g. Unauthorized (error code 401), Forbidden (403) or Internal Server Error (50X). Do you often find yourself checking if network connection is available before sending request? Well, we can handle that too and make your life easier by the way.
ErrorHandler to the rescue!
Retrofit provides us with the ErrorHandler interface, which instantiated we can pass to RestAdapter while building one.
Implementing ErrorHandler is easy, just create new class and implement the interface (which has only one method – handleError(RetrofitError cause)). Basic example (code kept simple for the sake of this article’s clarity):
BroadcastedException:
NetworkBroadcastedException:
UnauthorizedBroadcastedException:
ForbiddenBroadcastedException:
InternalServerErrorBroadcastedException:
Provided exceptions are created when sufficient conditions are met when retrofit receives error from server. Please note that error handling is being processed before entering failure() method of Callback.
NetworkBroadcastedException is a convenient way to handle network issues (no Internet access, timeouts). UnauthorizedBroadcastedException lets you keep logout/relog user code (context reference helps with that) in one place when 401 Unauthorized is received. ForbiddenBroadcastedException gathers the code responsible for handling access forbidden errors. InternalServerErrorBroadcastedException serves the similar purpose but regarding internal server errors.
As you can see exceptions broadcast messages using intents. To receive them in your let’s say Fragments you have to register broadcast receiver for given exception. You can do it for every Fragment class that you create but this will make you duplicate code blocks and introduce clutter. I will show you how to do it better.
Finalizing
First, you need some interfaces like this:
Then, create new class that extends Fragment class and make it implement given interface:
Now you can create your fragments by extending CoreFragment and handle received network exception broadcasts by overriding processNetworkException() method in your fragments. For other exceptions (like UnauthorizedException, ForbiddenException, … ) the code looks almost the same – just remember to add relevant interfaces and dispatch methods in processException()’s switch.
Summary
Retrofit is a great library that can help you build better, maintainable code and avoid code duplication. Presented code snippets show that you can easily do that while separating business logic from handling I/O (network) errors as well.
I hope simple hints I presented will make your developer’s life easier. Have fun and create great things!
Image used in the article by photoeverywhere.co.uk