Retrofit library

Retrofit is a type-safe REST client for Java and Android. It requires at minimum Java 8+ or Android API 21+.

Annotations are used to describe the HTTP request:

  • URL parameter replacement and query parameter support
  • object conversion to request body (e.g., JSON, protocol buffers)
  • multipart request body and file upload

Library page on GitHub. Official doc is here.

Add dependency

implementation("com.squareup.retrofit2:retrofit:2.9.0")

1. First define interface of REST service.

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
  
  @Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
  })
  @GET("users/{username}")
  Call<User> getUser(@Path("username") String username);
  
  @POST("users/new")
  Call<User> createUser(@Body User user);
  
  @FormUrlEncoded
  @POST("user/edit")
  Call<User> updateUser(@Field("first_name") String first, 
      @Field("last_name") String last);
      
  @Multipart
  @PUT("user/photo")
  Call<User> updateUser(@Part("photo") RequestBody photo, 
      @Part("description") RequestBody description);    
  // ...
}

2. Generate an implementation of the interface.

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

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

3. Create Call objects to perform synchronous or asynchronous HTTP requests to the remote webserver.

Call<List<Repo>> repos = service.listRepos("octocat");

// synchronous
Response response = repos.execute();

// asynchronous
repos.enqueue(new Callback<List<Repo>>() {
    @Override
    public void onResponse(Call<List<Repo>> call,
            Response<List<Repo>> response) {
          // request is success, but check response.body() on null
          // ... 
    }
    @Override
    public void onFailure(Call<List<Repo>> call, Throwable t) {
        // error
    }
});

convertes

Retrofit provides different converters to (de)serialize the data. To do this, add the following dependency:

  • Gson: com.squareup.retrofit2:converter-gson
  • Jackson: com.squareup.retrofit2:converter-jackson
  • Moshi: com.squareup.retrofit2:converter-moshi
  • Protobuf: com.squareup.retrofit2:converter-protobuf
  • Wire: com.squareup.retrofit2:converter-wire
  • Simple XML: com.squareup.retrofit2:converter-simplexml
  • JAXB: com.squareup.retrofit2:converter-jaxb
  • Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars
// Gson allows to convert json response 
// to the java object
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .addConverterFactory(GsonConverterFactory.create())
    .build();

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

adapters

Retrofit ships with a default adapter for executing Call instances. But you can use other popular execution mechanisms like RxJava, Guava, Java 8 (see additional retrofit adapters on github).

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com")
    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
    .build();

retrofit with coroutines

Recent versions of Retrofit support Kotlin coroutines. Just add suspend keyword to the inteface functions.

You can also wrap the result type with a Response class to execute request in current context.


interface GitHubService {
  @GET("users/{user}/repos")
  suspend fun listRepos(@Path("user") user: String): Response<List<Repo>>

}
class MyViewModel: ViewModel() {
    // ...
    val userName: String
    val repoList = MutableLiveData<List<Repo>>>()
    val isRepoLoading = MutableLiveData<Boolean>()
    val errorMessage = MutableLiveData<String>()

    // ...
    fun loadRepo() {
        viewModelScope.launch {
            val response = gitHubService.listRepos(userName)
            withContext(Dispatchers.Main) { // switch to main thread
                if (response.isSuccessful) { // if ok set new values
                    repoList.postValue(response.body())
                    loading.value = false
                } else {
                    onError("Error : ${response.message()} ")
                }
            }
        }
    }

    private fun onError(message: String) {
        errorMessage.value = message
        loading.value = false
    }
}