Implementing Simple Promise-like Adapter call API in Retrofit for Android

Mohammed Hewedy
2 min readJun 13, 2021

With no doubt, Retrofit is the most common API client for android. not only this, it inspires many other libraries on the backend side, e.g. feign and my own spwrap library.

Retrofit comes with pluggable architecture when it comes to processing the response, which is called CallAdapter s.

By default, Retrofit uses ExecutorCallAdapterFactory that process the request on OkHTTP executor service, then invoke the user’s code callback on the android main thread executor(MainThreadExecutor ).

The one thing about the default CallAdaptor I don’t like is the callback mechanism. being in 2021 and the callback cannot utilize the Java 8 functional interfaces, make me feel sick 😂.

Of course one can replace this adapter with the RxJava adapter, but I am trying to find a simple enough solution to the problem. so maybe RxJava is more suitable for more complex cases. (maybe Retrofit guava adaptor is more fit here, but all ways, I went ahead and wrote my own).

I decided to write a simple CallAdapter I called (PromiseCallAdapter) that allow the caller to write code that looks like (instead of enqueue(someCallback):

Instead of:

userClient.login(new User(phone, password))
.enqueue(new Callback<LoginResponse>() {
@Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
//..... call onLoginSuccess if success otherwise call onFailure
}

@Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
// ..... call onFailure
}
});

You will write:

userClient.login(new User(phone, password))
.then(response -> onLoginSuccess(response))
.error(apiError -> onFailure(apiError));

Here’s the full implementation:

You will need to register it with Retrofit like this:

retrofit = new Retrofit.Builder()
.baseUrl("http://example.com/api/v1/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(Promise.PromiseCallAdapterFactory.INSTANCE)
.build();

And then write the HTTP Client interface like:

interface UserClient {

@POST("login")
Promise<LoginResponse> login(@Body User user);

@POST("register")
Promise<Void> register(@Body User user);
}

And then do the call like this:

userClient.login(new User(phone, password))
.then(response -> onLoginSuccess(response))
.error(apiError -> onFailure(apiError));

That’s all folks!

--

--