Quick and easy guide to Retrofit 2.0 setup or migration with RxJava

You might have noticed that the final Retrofit 2.0 is out, so what better moment to upgrade than now? If you’ve seen the Retrofit home page, you have also noticed that the documentation is even worse than before, and migration guides are sorely lacking. I did find a few useful guides, but I still lost time trying to figure things out. Hence this guide.

How much time will you need to upgrade? If you just have simple calls where you don’t need to examine the HTTP status codes etc, it should take less than an hour (with testing).

QUICK UPGRADE

STEP 1. Gradle

You probably have something like this:

compile ‘com.squareup.retrofit:retrofit:1.9.0’
compile ‘com.squareup.okhttp:okhttp-urlconnection:2.0.0’
compile ‘com.squareup.okhttp:okhttp:2.0.0’

New Retrofit has the OkHTTP library already integrated, so this will become one line:

compile ‘com.squareup.retrofit2:retrofit:2.0.0’

On the other hand, you will have to add some other things. Want to use RxJava callbacks?

compile ‘com.squareup.retrofit2:adapter-rxjava:2.0.0’

Use Gson as your converter?

com.squareup.retrofit2:converter-gson:2.0.0

There’s support for many more, like Jackson, Moshi, Protobuf, Wire, XML or simple scalars — check the Retrofit homepage for the list.

If you want to have the calls logged, it’s not done through Retrofit any more but using the OkHTTP interceptors, so you will also need to add the OkHTTP logging interceptor:

com.squareup.okhttp3:logging-interceptor:3.1.2

STEP 2. API Interface

This is the quickest step — if you’re using RxJava and don’t need the HTTP response access, the return types will remain the same (Observable<YourObject>). Android Studio will still report errors though — what you need to do is to delete the old imports that used retrofit.* package and add new, retrofit2.* ones.

So, remove this:

import retrofit.http.GET;

and instead put this:

import retrofit2.http.GET;

STEP 3. Your API Client

This is the place where you create the client i.e. RestAdapter. This, for example, was my old RestAdapter in Retrofit 1.9:

RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(endpoint)
.setConverter(new GsonConverter(mGson))
.setClient(new OkClient(getOkHttpClient(context)))
.setLogLevel(RestAdapter.LogLevel.BASIC)
.setErrorHandler(mApiErrorHandler)
.setRequestInterceptor(request -> {
request.addHeader(“Authorization”, mApiKey);
})
.build();

New client is actually called Retrofit, and the setters have changed:

  • setEndpoint() -> baseUrl()
  • setConverter() -> addConverterFactory()
  • setClient()-> client()

There are no setLogLevel() and no setRequestInterceptor() (it’s done through OkHTTP now), but also there’s no setErrorHandler() any more! But I was already creating my OkClient anyway, so some of the code will get moved there. Here’s my new Retrofit client:

Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(endpoint)
.client(getOkHttpClient(context, true))
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();

The addCallAdapterFactory(RxJavaCallAdapterFactory.create()) is crucial if you use RxJava!

STEP 4. OkHttpClient

In the previous step, you might have noticed that I create my own OkHttpClient — maybe you didn’t need to do it before, but now that is almost a necessity: if you want logging or authorization headers, you have to make your own.

Here’s my old OkHttpClient:

private OkHttpClient getOkHttpClient(Context context) {
OkHttpClient okClient = new OkHttpClient();

final @Nullable File baseDir = context.getCacheDir();
if (baseDir != null) {
final File cacheDir = new File(baseDir, “HttpResponseCache”);
okClient.setCache(new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE));
}

okClient.setConnectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
okClient.setReadTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
okClient.setWriteTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);

return okClient;
}

I just used it for setting the file caching of HTTP calls and setting the timeouts. Now, it also has logging and header authorization. Also, note that it now uses the Builder pattern throughout:

private OkHttpClient getOkHttpClient(Context context) {
OkHttpClient.Builder okClientBuilder = new OkHttpClient.Builder();
    okClientBuilder.addInterceptor(headerAuthorizationInterceptor);
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel( HttpLoggingInterceptor.Level.BASIC);
okClientBuilder.addInterceptor(httpLoggingInterceptor);
    final @Nullable File baseDir = context.getCacheDir();
if (baseDir != null) {
final File cacheDir = new File(baseDir, “HttpResponseCache”);
okClientBuilder.cache(new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE));
}
    okClientBuilder.connectTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
okClientBuilder.readTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
okClientBuilder.writeTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS);
    return okClientBuilder.build();
}

First I create an OkHttpClient.Builder, then I add the header interceptor, then the logging interceptor, and finally the stuff that was previously there (file caching and timeouts). The order in which you add interceptors is important because they’re chained together — if I have added first the logging interceptor and then the header interceptor, it wouldn’t have logged the headers!

Here’s the headerAuthorizationInterceptor used above:

Interceptor headerAuthorizationInterceptor = new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Headers headers = request.headers().newBuilder().add(“Authorization”, mApiKey).build();
request = request.newBuilder().headers(headers).build();
return chain.proceed(request);
}
};

This is probably the biggest conceptual difference with new OkHttp — as I said, the Builder pattern is now used throughout, and all data is immutable, so you have to use builders to first build new header, and then a new request with the new header, which gets inserted in the chain.

STEP 5. Change your endpoint and resource URLs!

This is arguably the biggest and the most important change, because now Retrofit uses HTTP style resolving.That means that your endpoint should end with a slash (http://www.google.com/, and NOT http://www.google.com) and your call URLs mostly shouldn’t have a leading slash like before (mail and NOT /mail).

That’s because if you put a leading slash, it will resolve to the root domain, so if your endpoint is http://www.example.com/api/, and your resource URL is /test, the actual called URL will NOT be http://www.example.com/api/test but http://www.example.com/test! (more details here).

STEP 6. Compile and test

That’s it! If you didn’t have any advanced stuff in there, it should already compile and work. If it doesn’t, comment it out and test the things already done, your calls should work at this point.

SOME OTHER USEFUL TIPS

Here are some other things that I needed to add/change, that weren’t covered by the steps above. At this point I had 90% things covered, but there were still some things missing.

CUSTOM GSON INSTANCE

In the Step 3 above, we have set a GsonConverter:

addConverterFactory(GsonConverterFactory.create())

The problem with this is that if you use some custom type conversion, you probably already have your own Gson instance setup, like I did. Well, you can easily pass it in the constructor:

addConverterFactory(GsonConverterFactory.create(myGson))

RxJava SCHEDULER DECLARATION

This is a nice addition. Previously, with Retrofit 1.9, I would have to make all calls like this:

myApiService
.myCall()
.subscribeOn(Schedulers.io())
.subscribe(…);

Now, when adding a CallAdapterFactory, instead of using a normal creation method, like in Step 3:

addCallAdapterFactory(RxJavaCallAdapterFactory.create())

We can now pass a scheduler instance on which we want to execute the calls, using RxJavaCallAdapterFactory.createWithScheduler():

addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))

This way, I don’t have to put subscribeOn(Schedulers.io)) in every call anymore.

FULL URL CALLS

One of the biggest problems I had with the old Retrofit was that it was very hard to just call some URL and get the data — you had to do some tricks like creating the RestAdapter with an empty String reference and then changing that string when you have to execute the call, but also splitting the URL into base URL and resource URL. That RestAdapter also had to be separate from the main one.

Now, in your Api interface class, you can simply declare a call like this:

@GET
Observable<Object> executeFullUrlCall(@Url String url);

And when called through your Retrofit client, it will simply execute a GET of the url String that you have passed through!

ERROR HANDLING AND HTTP RESPONSE ACCESS

Another big news is that error handling is now up to you. You cannot set a global ErrorHandler like before, but you need to do it on a call-by-call basis. If you want access to the HTTP response or maybe your backend gives you a specific error object, you can now simply wrap your regular Object returned by the call inside a Response wrapper.

So, if you had a call that was returning Observable<String>, you can now change it into Observable<Response<String>>. Response object has a few useful methods:

  • isSuccessful() — distinguishes between 2xx and others (like old ErrorHandler did)
  • code() — gives you the HTTP response code
  • body() — gives you your normal response Object (e.g. String), already deserialized
  • headers() — HTTP response headers
  • errorBody() — this is a raw response body if the call was unsuccessful
  • message() — HTTP status message (or null)
  • raw() —raw response from the HTTP client

So if you need to distinguish between different 2xx codes or need access to objects returned on error, this gives you unprecendented access to those.

FACEBOOK STETHO SETUP/UPDATE

In my app I have also used Facebook Stetho (A debug bridge for Android applications), which I was adding to the OkHttpClients in my RestAdapters to be able to debug them. Since Retrofit 2.0 uses OkHttp 3.0, Stetho also needed to be updated.

First, replace these in your build.gradle file:

compile ‘com.facebook.stetho:stetho:1.2.0’
compile ‘com.facebook.stetho:stetho-okhttp:1.2.0’

with the new versions:

compile ‘com.facebook.stetho:stetho:1.3.1’
compile ‘com.facebook.stetho:stetho-okhttp3:1.3.1’

And in your OkHttpClient setup:

okClientBuilder.addNetworkInterceptor(new StethoInterceptor());

replace this import:

import com.facebook.stetho.okhttp.StethoInterceptor;

with the new import:

import com.facebook.stetho.okhttp3.StethoInterceptor;

THAT’S IT!

Hope this was useful to you. Happy upgrade!