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
withObservable
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 theGsonConverterFactory
create for any custom data transformation purpose as well.
Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();