Last active
June 14, 2021 03:44
-
-
Save mhewedy/e297ff80692d54685cb212d308e81de4 to your computer and use it in GitHub Desktop.
Retrofit Promise-like (with then/error funcs) Call Adapter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.os.Handler; | |
import android.os.Looper; | |
import com.google.gson.Gson; | |
import java.lang.annotation.Annotation; | |
import java.lang.reflect.ParameterizedType; | |
import java.lang.reflect.Type; | |
import java.util.concurrent.Executor; | |
import java.util.function.Consumer; | |
import retrofit2.Call; | |
import retrofit2.CallAdapter; | |
import retrofit2.Callback; | |
import retrofit2.Response; | |
import retrofit2.Retrofit; | |
/** | |
* Promise-based Retrofit call adapter. <p> | |
* Example: | |
* <pre> | |
* messageClient.getLatest() | |
* .then(this::onGetLatestSuccess) | |
* .error(this::onFailure); | |
* </pre> | |
* | |
* @param <R> | |
*/ | |
public final class Promise<R> { | |
private static final Gson gson = new Gson(); | |
private static final Executor mainThreadExecutor = new MainThreadExecutor(); | |
private final Call<R> delegate; | |
private Consumer<R> respFunc; | |
private Consumer<ApiError> errFunc; | |
private final Callback<R> callback = new Callback<R>() { | |
@Override | |
public void onResponse(Call<R> call, Response<R> response) { | |
if (response.isSuccessful()) { | |
mainThreadExecutor.execute(() -> respFunc.accept(response.body())); | |
} else { | |
if (errFunc == null) { | |
return; | |
} | |
ApiError apiError; | |
if (response.errorBody() != null) { | |
apiError = gson.fromJson(response.errorBody().charStream(), ApiError.class); | |
} else { | |
apiError = gson.fromJson(response.message(), ApiError.class); | |
} | |
if (apiError == null) { | |
apiError = new ApiError(); | |
apiError.message = "Error occurred"; | |
} | |
ApiError finalApiError = apiError; | |
mainThreadExecutor.execute(() -> errFunc.accept(finalApiError)); | |
} | |
} | |
@Override | |
public void onFailure(Call<R> call, Throwable t) { | |
if (errFunc == null) { | |
return; | |
} | |
ApiError apiError = new ApiError(); | |
apiError.message = t.getMessage(); | |
mainThreadExecutor.execute(() -> errFunc.accept(apiError)); | |
} | |
}; | |
private Promise(Call<R> delegate) { | |
this.delegate = delegate; | |
} | |
public Error then(Consumer<R> respFunc) { | |
this.respFunc = respFunc; | |
this.delegate.enqueue(this.callback); | |
return new Error(); | |
} | |
public class Error { | |
public void error(Consumer<ApiError> errFunc) { | |
Promise.this.errFunc = errFunc; | |
} | |
} | |
// ---- setup classes --------------- | |
public static class PromiseCallAdapterFactory extends CallAdapter.Factory { | |
public static final PromiseCallAdapterFactory INSTANCE = new PromiseCallAdapterFactory(); | |
@Override | |
public CallAdapter get(Type returnType, Annotation[] annotations, Retrofit retrofit) { | |
Class<?> rawType = getRawType(returnType); | |
if (rawType == Promise.class && returnType instanceof ParameterizedType) { | |
Type callReturnType = getParameterUpperBound(0, (ParameterizedType) returnType); | |
return new CallAdapter() { | |
@Override | |
public Type responseType() { | |
return callReturnType; | |
} | |
@Override | |
public Object adapt(Call call) { | |
return new Promise<>(call); | |
} | |
}; | |
} | |
return null; | |
} | |
} | |
private static class MainThreadExecutor implements Executor { | |
private final Handler handler = new Handler(Looper.getMainLooper()); | |
@Override | |
public void execute(Runnable r) { | |
handler.post(r); | |
} | |
} | |
// this is the object returned from the api in case of error, change to your own | |
public static class ApiError { | |
public String message; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment