自定义呼叫适配器改造中的通用类型

Posted

技术标签:

【中文标题】自定义呼叫适配器改造中的通用类型【英文标题】:Generic type in retrofit for the custom call adapter 【发布时间】:2018-06-02 20:48:53 【问题描述】:

改造异步请求是回调,有 2 个方法 onResponse() 和 onFailure()。

我不想总是重写这两种方法并处理错误情况。

所以当我想通过 ApiResponse 的谷歌 GithubBrowserSample 包装改造响应正文并将错误转换如下:

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;
    

我也想用 Gson 转换器来转换改造响应,然后用 ApiResponse 包装。

如果我喜欢使用

Call<ApiResponse<Result>> requestCall = webClient.request1(xxx,xxx);
requestCall.enqueue(new Callback<ApiResponse<Result>> );

好像不行。 json 响应数据无法解析为 Result 对象。

所以我考虑编写我自己的自定义呼叫适配器引用 retrofit sample 来替换改造呼叫。但是我仍然有转换泛型类型的问题。

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);


以上代码在处理双参数泛型类型时存在问题。我不知道如何处理它。

同样运行这段代码也是崩溃

  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) 

有人可以帮助如何让MyCall&lt;ApiResponse&lt;Result&gt;&gt;MyCallback&lt;ApiResponse&lt;Result&gt;&gt; 调用入队吗?结果是用Gson转换器解析json数据内容。

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);
    

有人可以帮我指出问题吗?

【问题讨论】:

@Override public Type responseType() return null; 在该方法中返回 responseType @Rahul,非常感谢您指出。我现在修好了。 不客气 【参考方案1】:

修改 MyCallAdapter、MyCallback 和 MyCallImpl。 @Rahul 指出响应类型,现在一切正常。

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);
    

以上是正确的实现。是的。

【讨论】:

我们如何调用才能使 api 命中?

以上是关于自定义呼叫适配器改造中的通用类型的主要内容,如果未能解决你的问题,请参考以下文章

Android 屏幕适配屏幕适配通用解决方案 ⑤ ( 自定义组件解决方案 | 自定义 ViewGroup 组件 onMeasure 方法中计算每个子组件坐标数据 | 自定义组件完整代码 )

EditText.SetText() 在自定义适配器中更改我的软键盘输入类型

Android 屏幕适配屏幕适配通用解决方案 ③ ( 自定义组件解决方案 | 获取设备状态栏高度 | 获取设备屏幕数据 )

Android 屏幕适配屏幕适配通用解决方案 ③ ( 自定义组件解决方案 | 获取设备状态栏高度 | 获取设备屏幕数据 )

Android 屏幕适配屏幕适配通用解决方案 ④ ( 自定义组件解决方案 | 计算设计稿与实际布局的比例系数 )

Android 屏幕适配屏幕适配通用解决方案 ④ ( 自定义组件解决方案 | 计算设计稿与实际布局的比例系数 )