Retrofit 2

Jintin
3 min readDec 3, 2017

--

Retrofit is a type-safe HTTP client for Android and Java. With Retrofit we can compose the HTTP connection easily through a simple expressive interface just like an api document. Besides the elegant syntax it provides, it’s also easy to incorporate with different library.

Ready to go? Let’s see how to define our api first.

Code as Document

public interface GitHubService {
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
@GET("group/{id}/users")
Call<List<User>> groupList(
@Path("id") int groupId,
@Query("sort") String sort);
@POST("users/new")
Call<User> createUser(@Body User user);
}

Retrofit use Java interface to get rid of the logic from describing the api like a charm. Different function represents different api request in Retrofit.

Each function use annotation like @GET, @POST, @PUT, @DELETE or @HEAD to indicate different HTTP method with the api path definition by following where the path can be a full path or excluding the domain as well.

Each parameter has its own annotation to distinguish different role in the connection like @Path for path parameter, @Query for query string and @Body for post body, etc.

Every function will return a Call object which wrap the desire object we want.

With this simple declaration, we can let everyone easily understand and maintain our api just like a normal api document. That is the magic of Retrofit. Next step we’ll see how to use our interface to create connection by Retrofit.

Retrofit Client

We’ll instantiate the Retrofit object first with basic configuration.

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

The BASE_URL will apply to all of the api call except those path has domain definition already.

The ConverterFactory is the helper class to transform connection data to desire type as we define in the api interface. Here we use Gson but you can use any library or create your own if you like:

  • Gson: com.squareup.retrofit2:converter-gson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf

You can find all the support converters in https://mvnrepository.com/artifact/com.squareup.retrofit2

Now we can use the Retrofit object to generate GitHubService object

GitHubService service = retrofit.create(GitHubService.class);

Retrofit use Dynamic Proxy to intercept its method call and compose/dispatch the call in runtime internally like following:

public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}

After we get the service object, we can call each api as we defined in the interface previously.

try {
Response<List<Repo>> response =
service.listRepos("a").execute();
if (response.isSuccessful()) {
// success
List<Repo> repos = response.body();
} else {
// fail
}
} catch (IOException e) {
// fail
}

The Call object will return a Response object with same generic type as Call has after we execute it.

Asynchronous

The execute will execute in the same thread where it was. If you don’t like it, Retrofit also provide asynchronous way to handle it.

service.listRepos("a").enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
if (response.isSuccessful()) {
// success
} else {
// fail
}
}

@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
// fail
}
});

RxJava integration

If you are a big fan of RxJava, Retrofit also provide a easy way to integrate for you. You only have to do 3 things:

  • Add RxJava adapter(com.squareup.retrofit2:adapter-rxjava) dependency.
  • Add RxJavaCallAdapterFactory in Retrofit creation.
Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
  • replace the Call with Observable as in RxJava.
@GET("users/{user}/repos")
Observable<List<Repo>> listRepos(@Path("user") String user);

That’s all. The api will return Observable as you might familiar with.

Customization

  • You can inject your own HttpClient like OkHttpClient for your customize logging or cache purpose.
Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build();
  • You can inject your own Gson during the GsonConverterFactory create for any custom data transformation purpose as well.
Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();

Reference

--

--

Jintin

Android/iOS developer, husband and dad. Love to build interesting things to make life easier.