Retrofit is a great HTTP client for Android (and Java) and the second major release will be available by the end of 2015. We already published an extensive series on Retrofit in the end of 2014 and beginning of 2015. Due to the popularity of this series, we decided to write this upgrade guide from 1.9
(or any other Retrofit 1.x
version) to 2.0
or newer. This guide will help you push your app to the next version of Retrofit since there are multiple breaking changes when jumping to the upcoming version 2.
If you aren’t familiar with our Retrofit series, have a look at the overview of topics. We’ll update all posts in this series and include examples for Retrofit 2 besides the ones for Retrofit 1.9. To be clear: we won’t delete existing posts or code snippets for Retrofit 1.x
, but add a corresponding snippet for Retrofit 2 where necessary. We’ll update the blog posts once the internal Retrofit API is stable and methods won’t change anymore.
Retrofit Series Overview
- Getting Started and Creating an Android Client
- Basics of API Description
- Creating a Sustainable Android Client
- URL Handling, Resolution and Parsing
- How to Change API Base Url at Runtime
- Multiple Server Environments (Develop, Staging, Production)
- Share OkHttp Client and Converters between Retrofit Instances
- Upgrade Guide from 1.9
- Beyond Android: Retrofit for Java Projects
- How to use OkHttp 3 with Retrofit 1
- Synchronous and Asynchronous Requests
- Send Objects in Request Body
- Add Custom Request Header
- Manage Request Headers in OkHttp Interceptor
- Dynamic Request Headers with @HeaderMap
- Multiple Query Parameters of Same Name
- Optional Query Parameters
- Send Data Form-Urlencoded
- Send Data Form-Urlencoded Using FieldMap
- How to Add Query Parameters to Every Request
- Add Multiple Query Parameter With QueryMap
- How to Use Dynamic Urls for Requests
- Constant, Default and Logic Values for POST and PUT Requests
- Cancel Requests
- Reuse and Analyze Requests
- Optional Path Parameters
- How to Send Plain Text Request Body
- Customize Network Timeouts
- How to Trust Unsafe SSL certificates (Self-signed, Expired)
- Dynamic Endpoint-Dependent Interceptor Actions
- How to Update Objects on the Server (PUT vs. PATCH)
- How to Delete Objects on the Server
- Introduction to (Multiple) Converters
- Adding & Customizing the Gson Converter
- Implementing Custom Converters
- How to Integrate XML Converter
- Access Mapped Objects and Raw Response Payload
- Supporting JSON and XML Responses Concurrently
- Handling of Empty Server Responses with Custom Converter
- Send JSON Requests and Receive XML Responses (or vice versa)
- Unwrapping Envelope Responses with Custom Converter
- Wrapping Requests in Envelope with Custom Converter
- Define a Custom Response Converter
Introduction
Retrofit finally got the second major release in March of 2016. Retrofit 2 comes with various fundamental changes and also includes breaking changes to the internal API. That requires you to update your code related to Retrofit when jumping on Retrofit 2.
Maven & Gradle Dependencies
Retrofit is available as Maven and Gradle dependencies. As within Retrofit 1, you need to import the underlying HTTP client. By default, Retrofit 2 leverages OkHttp for the job which is already defined as a dependency of Retrofit 2 itself.
Gradle: Retrofit & OkHttp
compile 'com.squareup.retrofit2:retrofit:2.2.0'
If you don’t want to rely on the OkHttp peer-dependency shipped with Retrofit 2, you can import the desired version of OkHttp yourself. To avoid confusion and doubled imports of OkHttp, tell gradle to explicitly exclude Retrofit’s dependency.
compile ('com.squareup.retrofit2:retrofit:2.2.0') {
// exclude Retrofit’s OkHttp peer-dependency module and define your own module import
exclude module: 'okhttp'
}
compile 'com.squareup.okhttp3:okhttp:3.6.0'
Maven: Retrofit & OkHttp
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>3.6.0</version>
</dependency>
Retrofit 2 doesn’t ship with Gson by default. Before, you didn’t need to worry about any integrated converter and you could use Gson out of the box. This library change affects your app and you need to import a converter as a sibling package as well. We’ll touch the converter later within this post and show you how to config the Gson or any other response converter for your app.
Converters
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
Also RxJava isn’t integrated by default anymore. You need to add this additional import to your app’s dependencies to get the reactive functionality back in the app.
RxJava
compile 'com.squareup.retrofit2:adapter-rxjava:2.2.0'
compile 'io.reactivex:rxandroid:1.0.1'
RestAdapter —> Retrofit
The previously named RestAdapter
class is renamed to Retrofit
. The builder pattern is still available and you can easily chain available methods to customize the default behaviour.
Retrofit 1.9
RestAdapter.Builder builder = new RestAdapter.Builder();
Retrofit 2.x
Retrofit.Builder builder = new Retrofit.Builder();
setEndpoint —> baseUrl
You already read about the renaming of RestAdapter
to Retrofit
. There is another change within the Retrofit
class which affects the base url (previously named endpoint url). Within Retrofit 1, the setEndpoint(String url)
method defines the API’s base url which is used later when defining partial routes within the interface declaration.
Retrofit 1.9
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(API_BASE_URL);
.build();
YourService service = adapter.create(YourService.class);
Within Retrofit 2, the method is renamed to baseUrl(String url)
. It still defines the base url for your API.
Note: Before you can call the build()
method on the Retrofit.Builder
, you need at least define the base url.
Retrofit 2.x
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_BASE_URL);
.build();
YourService service = retrofit.create(YourService.class);
There is another major change in the API url handling. The next section explains the changes in more detail.
Base Url Handling
There is a completely new url handling within Retrofit 2. This is very important to understand when updating from 1.x
to 2.x
!
Previously, the defined endpoint was always used as the default url for requests. Within your interface declaration which represent the individual API endpoints, you defined your partial routes including query or path parameters, request body or multiparts.
The API endpoint url and the partial url then were concatenated to the final url where the request is sent. To illustrate all theory, let’s look at an example.
Retrofit 1.x
public interface UserService {
@POST("me")
User me();
}
RestAdapter adapter = RestAdapter.Builder()
.setEndpoint("https://your.api.url/v2/");
.build();
UserService service = adapter.create(UserService.class);
// the request url for service.me() is:
// https://your.api.url/v2/me
Within Retrofit 2.x
, you need to adjust your mind when it comes to API base urls. Retrofit 1 just concatenated the defined string values which resulted in a final request url. This behavior changes in Retrofit 2 since now the request url is created using the HttpUrl.resolve()
method. This will create links similar to the well known <a href>
.
To get things straight, look at the following code snippet which illustrates new way to resolve urls.
Retrofit 2.x
public interface UserService {
@POST("/me")
Call<User> me();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://your.api.url/v2");
.build();
UserService service = retrofit.create(UserService.class);
// the request url for service.me() is:
// https://your.api.url/me
You see, the leading /
within the partial url overrides the /v2
API endpoint definition. Removing the /
from the partial url and adding it to the base url will bring the expected result.
public interface UserService {
@POST("me")
Call<User>me();
}
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://your.api.url/v2/");
.build();
UserService service = retrofit.create(UserService.class);
// the request url for service.me() is:
// https://your.api.url/v2/me
Dynamic Urls
This is one of the features you won’t immediately have a use-case in mind. However, we can provide you a use-case which we experienced during the implementation of a feature within one of our apps. We wanted to download a .zip
file from an internet source and the files will have different urls. The files are either stored on Amazon’s S3 or somewhere else on the web. Our problem was, that we need to create a RestAdapter
with the respective base url every time we wanted to load a file. This gives you headache because you need to instantiate multiple HTTP clients and this is definitely bad practice.
Finally, the painful time will find its end and with Retrofit 2 we can use dynamic urls for an endpoint. You can leave the HTTP verb annotation empty and use the @Url
annotation as a method parameter. Retrofit will map the provided url string and do the job for you.
public interface UserService {
@GET
public Call<File> getZipFile(@Url String url);
}
OkHttp Integrated
You’ve seen at the beginning of this post, that you don’t necessarily need to import OkHttp besides Retrofit itself. Retrofit 2 relies on OkHttp as the HTTP client and has its own dependency to the library as well.
Within Retrofit 1, you could set OkHttp manually as the HTTP client of choice. Now, OkHttp is required to use the Call
class where responses get encapsulated.
If you want to import a specific version of OkHttp and don’t use the one shipped with Retrofit, use the specific gradle import to exclude Retrofit’s built-in dependency to OkHttp:
compile ('com.squareup.retrofit2:retrofit:2.2.0') {
// exclude Retrofit’s OkHttp peer-dependency module and define your own module import
exclude module: 'okhttp'
}
compile 'com.squareup.okhttp3:okhttp:3.6.0'
Maven will automatically remove the peer-dependency if you specify OkHttp as a dependent module in your project.
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>3.6.0</version>
</dependency>
Interceptors Powered by OkHttp
Interceptors are a powerful way to customize requests with Retrofit. This feature was beneficial in Retrofit 1 and so will it be in version 2. A common use-case where you want to intercept the actual request is to add individual request headers. Depending on the API implementation, you’ll want to pass the auth token as the value for the Authorization
header.
Since Retrofit heavily relies on OkHttp, you need to customize the OkHttpClient
and add an interceptor. The following code snippet illustrates how to add a new interceptor which uses the adds the Authorization
and Accept
headers to original request and proceeds with the actual execution.
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
// Customize the request
Request request = original.newBuilder()
.header("Accept", "application/json")
.header("Authorization", "auth-token")
.method(original.method(), original.body())
.build();
Response response = chain.proceed(request);
// Customize or return the response
return response;
}
});
OkHttpClient client = httpClient.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://your.api.url/v2/");
.client(client)
.build();
If you’re using a custom OkHttpClient
, you need to set the client within the Retrofit.Builder
by using the .client()
method. This will update the default client with the enhanced self-made version.
You can apply the interceptors for several use-cases like authentication, logging, request and response manipulation, and many more.
Synchronous & Asynchronous Requests
If you worked with Retrofit 1, you’re familiar with the different method declaration in the service interface. Synchronous methods required a return type. In contrast, asynchronous methods required a generic Callback
as the last method parameter.
Within Retrofit 2, there is no differentiation anymore for synchronous and asynchronous requests. Requests are now wrapped into a generic Call
class using the desired response type. The following paragraphs will show you the differences of Retrofit 1 vs. Retrofit 2 in respect to service declaration and request execution.
Interface Declaration
To differentiate a synchronous from an asynchronous request, you defined either the actual type or void
as the response type. The latter required you to pass a generic Callback
as the last method parameter. The following code snippet shows an exemplary interface declaration in Retrofit 1.
Retrofit 1.9
public interface UserService {
// Synchronous Request
@POST("/login")
User login();
// Asynchronous Request
@POST("/login")
void getUser(@Query String id, Callback<User> cb);
}
Within Retrofit 2, there is no different declaration anymore. The actual execution type is defined by the used method of the final Call
object.
Retrofit 2.x
public interface UserService {
@POST("/login")
Call<User> login();
}
Request Execution
Retrofit 1 handles the request execution by using either a return type for synchronous or a Callback
for asynchronous ones. If you worked with Retrofit before, you’re familiar with the following code block.
Retrofit 1.9
// synchronous
User user = userService.login();
// asynchronous
userService.login(new Callback<User>() {
@Override
public void success(User user, Response response) {
// handle response
}
@Override
public void failure(RetrofitError error) {
// handle error
}
});
There is a completely different request execution handling in Retrofit 2. Since every request is wrapped into a Call
object, you’re now executing the request using either call.execute()
for synchronous and call.enqueue(new Callback<>() {})
for asynchronous ones. The example code below shows you how to perform each request type.
Retrofit 2.x
// synchronous
Call<User> call = userService.login();
User user = call.execute().body();
// asynchronous
Call<User> call = userService.login();
call.enqueue(new Callback<User>() {
@Override
public void onResponse(Call<User> call, Response<User> response) {
// response.isSuccessful() is true if the response code is 2xx
if (response.isSuccessful()) {
User user = response.body();
} else {
int statusCode = response.code();
// handle request errors yourself
ResponseBody errorBody = response.errorBody();
}
}
@Override
public void onFailure(Call<User> call, Throwable t) {
// handle execution failures like no internet connectivity
}
}
Within Retrofit 2, the onResponse()
method is called even though the request wasn’t successful. The Response
class has a convenience method isSuccessful()
to check yourself whether the request was handled successfully (returning status code 2xx
) and you can use the response object for further processing. If the status code is not 2xx
, you need to handle errors yourself. If you’re expecting a response body in failure situations (like an error message), you can parse the object yourself by using the errorBody()
method of the ResponseBody
class.
Cancel Requests
With Retrofit 1, there was no way to cancel requests even if they weren’t executed yet. This changes in Retrofit 2 and you’re finally able to cancel any request if the HTTP scheduler didn’t executed it already.
Call<User> call = userService.login();
User user = call.execute().body();
// changed your mind, cancel the request
call.cancel();
Doesn’t matter if you’re performing a synchronous or asynchronous request, OkHttp won’t send the request if you’re changing your mind fast enough.
No Default Converter
The previous version 1 of Retrofit shipped with Gson integrated as the default JSON converter. The upcoming release won’t have any default converter integrated anymore. You need to define your preferred converter as a dependency within your project. If you want to use Gson, you can use the following gradle import to define the sibling module:
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
There are multiple other converters available. The following list shows the required imports to get the updated converters for Retrofit 2.
Available Converters
- Gson:
com.squareup.retrofit2:converter-gson:2.2.0
- Moshi:
com.squareup.retrofit2:converter-moshi:2.2.0
- Jackson:
com.squareup.retrofit2:converter-jackson:2.2.0
- SimpleXML:
com.squareup.retrofit2:converter-simplexml:2.2.0
- ProtoBuf:
com.squareup.retrofit2:converter-protobuf:2.2.0
- Wire:
com.squareup.retrofit2:converter-wire:2.2.0
If none of the above listed converters fit your need, you can create your own one by implementing the abstract class Converter.Factory
. In case you need help, lean on the available Retrofit converter implementations.
Add Converter to Retrofit
We need to manually add the desired converters to Retrofit. The section above describes how to import a given converter module and additionally, you need to plug one ore many ConverterFactory
’s into the Retrofit
object.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://your.api.url/v2/");
.addConverterFactory(ProtoConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
The code example above plugs two converters to Retrofit. The order in which you specify the converters matters! Let’s assume the following scenario which clarifies the importance of converter order. Since protocol buffers may be encoded in JSON, Retrofit would try to parse the data with Gson if it was defined as the first converter. But what we want is that the proto converter tries to parse the data first and if it can’t handle it, pass it to the next converter (if there is another one available).
RxJava Integration
Retrofit 1 already integrated three request execution mechanisms: synchronous, asynchronous and RxJava. Within Retrofit 2, only synchronous and asynchronous requests are still available by default. Hence, the Retrofit development team created a way to plug additional execution mechanisms into Retrofit. You’re able to add multiple mechanisms to your app like RxJava or Futures.
To get the RxJava functionality back into Retrofit 2, you need to import the following two dependencies. The first dependency is the RxJava CallAdapter
which lets Retrofit know that there is a new way to handle requests. Precisely, that means you can exchange the Call<T>
by CustomizedCall<T>
. In case of RxJava, we’ll change Call<T>
with Observable<T>
.
The second dependency is required to get the AndroidSchedulers
class which is needed to subscribe code on Android’s main thread.
Gradle Dependencies
compile 'com.squareup.retrofit2:adapter-rxjava:2.2.0'
compile 'io.reactivex:rxandroid:1.0.1'
The next thing required is to add the new CallAdapter
to the Retrofit
object before creating the service instance.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl);
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
Code is better than all the theory. That’s why we touch the next code example to illustrate the changes when using RxJava. At first, we declare a service interface. Afterwards, we assume to have a userService
instance created and can directly leverage the Observable to observe on Android’s main thread. We also pass a new Subscriber to the subscribe
method which will finally provide the successful response or error.
public interface UserService {
@POST("/me")
Observable<User> me();
}
// this code is part of your activity/fragment
Observable<User> observable = userService.me();
observable
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<User>() {
@Override
public void onCompleted() {
// handle completed
}
@Override
public void onError(Throwable e) {
// handle error
}
@Override
public void onNext(User user) {
// handle response
}
});
No Logging by Default (But We’ve Got You Covered!)
Actually, there is also sad news: no logging in Retrofit 2 anymore. The development team removed the logging feature. To be honest, the logging feature wasn’t that reliable anyway. Jake Wharton explicitly stated that the logged messages or objects are the assumed values and they couldn’t be proofed to be true. The actual request which arrives at the server may have a changed request body or something else.
Even though there is no integrated logging by default, you can leverage any Java logger and use it within a customized OkHttp interceptor.
Conclusion
This was kind of an extensive overview of remarkable changes from Retrofit 1 to Retrofit 2 which require your attention and hands on code. The official change log depicts all new features, improvements and fixes for each release. If you’re interested in a specific thing, just search within the log.
Enjoy the upgrade to Retrofit 2 and working with the next release. We definitely think all changes are well thought out and the breaks are worth to lift Retrofit to a new level.
Additional Resources
- Jake Wharton’s talk @ Droidcon NYC 2015 on Retrofit 2
- Details on OkHttp interceptors
- Retrofit Converters
- RxAndroid on GitHub
- Retrofit CallAdapters
- Retrofit Change Log