Reputation: 1395
Retrofit async request is callback with 2 methods onResponse() and onFailure().
I don't want to always override these 2 methods and handle for the error case.
So when i want to through the google GithubBrowserSample of ApiResponse which wrapping the retrofit response body and convert the error as below:
public class ApiResponse<T> {
public final int code;
@Nullable
public final T body;
@Nullable
public final String errorMessage;
public ApiResponse(Throwable error) {
code = -1;
body = null;
if (error instanceof IOException) {
errorMessage = "No network error";
}
else {
errorMessage = error.getMessage();
}
}
public ApiResponse(Response<T> response) {
code = response.code();
if (response.isSuccessful()) {
body = response.body();
errorMessage = null;
}
else {
String message = null;
if (response.errorBody() != null) {
try {
message = response.errorBody().string();
} catch (IOException e) {
e.printStackTrace();
}
}
if (message == null || message.trim().length() == 0) {
message = response.message();
}
errorMessage = message;
body = null;
}
}
public boolean isSuccessful() {
return code >= 200 && code < 300;
}
}
Ialso want to use the Gson convertor to convert the retrofit response and then wrap it with ApiResponse.
If i use like
Call<ApiResponse<Result>> requestCall = webClient.request1(xxx,xxx);
requestCall.enqueue(new Callback<ApiResponse<Result>> {});
It seems not work. The json response data can't be parsed into the Result object.
So i think about writing my own custom Call Adapter referencing retrofit sample to replace with Retrofit Call. But i still have the problem in converting generic type.
public class MyCallAdapterFactory extends CallAdapter.Factory {
@Nullable
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != MyCall.class) {
return null;
}
Type observableType = getParameterUpperBound(0, (ParameterizedType) returnType);
Class<?> rawObservableType = getRawType(observableType);
if (rawObservableType != ApiResponse.class) {
throw new IllegalArgumentException("type must be a resource");
}
if (! (observableType instanceof ParameterizedType)) {
throw new IllegalArgumentException("resource must be parameterized");
}
Type bodyType = getParameterUpperBound(0, (ParameterizedType) observableType);
Executor executor = retrofit.callbackExecutor();
return new MyCallAdapter<>(bodyType, executor);
}
}
public class MyCallAdapter<T> implements CallAdapter<T, MyCall<T>> {
private final Type responseType;
private final Executor callbackExecutor;
public MyCallAdapter(Type responseType, Executor callbackExecutor) {
this.responseType = responseType;
this.callbackExecutor = callbackExecutor;
}
@Override
public Type responseType() {
return null;
}
@Override
public MyCall<T> adapt(Call<T> call) {
return new MyCallImpl<>(call, callbackExecutor);
}
}
public class MyCallImpl<T> implements MyCall<T> {
private final Call<T> call;
private final Executor callbackExecutor;
MyCallImpl(Call<T> call, Executor callbackExecutor) {
this.call = call;
this.callbackExecutor = callbackExecutor;
}
@Override
public void enqueue(MyCallback<T> callback) {
call.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
/* This is the problem. it will seems wrap to ApiResponse<ApiResponse<Result>>> because T is <ApiResponse<Result>>.
*/
callback.onResult(new ApiResponse<>(response));
}
@Override
public void onFailure(Call<T> call, Throwable t) {
/** This one is also the issue. */
callback.onResult(new ApiResponse<>(t));
}
});
}
@Override
public void cancel() {
call.cancel();
}
@Override
public MyCall<T> clone() {
return new MyCallImpl<>(call.clone(), callbackExecutor);
}
}
public interface MyCallback<T> {
void onResult(ApiResponse<T> response);
}
Above code has the problem in handling double parameterized generic type. I don't know how to handle it.
Also running this code is also crash
Caused by: java.lang.NullPointerException: type == null
at retrofit2.Utils.checkNotNull(Utils.java:286)
at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:324)
at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:313)
at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:736)
at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:169)
Could someone help how to let MyCall<ApiResponse<Result>>
to call enqueue with MyCallback<ApiResponse<Result>>
? Result is the parsing of the json data content with Gson converter.
public class MyCallAdapter<T> implements CallAdapter<T, MyCall<ApiResponse<T>>> {
public MyCall<ApiResponse<T>> adapt(Call<T> call) {
/* This one will have the problem after changing MyCall<T> to MyCall<ApiResponse<T>>, Parameterized type mismatch.*/
return new MyCallImpl<>(call, callbackExecutor);
}
}
Can someone help me point out the issue?
Upvotes: 2
Views: 7118
Reputation: 1395
Modify the MyCallAdapter, MyCallback and the MyCallImpl. @Rahul points out the response type and now everything work fine.
public class MyCallAdapter<T> implements CallAdapter<T, MyCall<ApiResponse<T>>> {
private final Type responseType;
private final Executor callbackExecutor;
public MyCallAdapter(Type responseType, Executor callbackExecutor) {
this.responseType = responseType;
this.callbackExecutor = callbackExecutor;
}
@Override
public Type responseType() {
return responseType;
}
@Override
public MyCall<ApiResponse<T>> adapt(Call<T> call) {
return new MyCallImpl<>(call, callbackExecutor);
}
}
public interface MyCallback<T> {
void onResult(T response);
}
public class MyCallImpl<T> implements MyCall<ApiResponse<T>> {
private final Call<T> call;
private final Executor callbackExecutor;
MyCallImpl(Call<T> call, Executor callbackExecutor) {
this.call = call;
this.callbackExecutor = callbackExecutor;
}
@Override
public void enqueue(MyCallback<ApiResponse<T>> callback) {
call.enqueue(new Callback<T>() {
@Override
public void onResponse(Call<T> call, Response<T> response) {
callback.onResult(new ApiResponse<>(response));
}
@Override
public void onFailure(Call<T> call, Throwable t) {
callback.onResult(new ApiResponse<>(t));
}
});
}
@Override
public void cancel() {
call.cancel();
}
@Override
public MyCall<ApiResponse<T>> clone() {
return new MyCallImpl<>(call.clone(), callbackExecutor);
}
}
This above is the correct implementation. Yeah.
Upvotes: 1