retrofit-helper 简洁的封装retrofit,优雅的取消请求

retrofit-helper

Retrofit是许多android开发者都在运用的Http恳求库!他负责网络恳求接口的封装,底层完成是OkHttp,它的一个特点是包含了特别多注解,方便简化你的代码量,CallAdapter.Factory 和Converter.Factory能够很灵活的扩展你的恳求。咱们在运用的时候还是需求封装一层便于咱们运用,retrofit-helper的作用就是再次简化你的恳求。

1. Retrofit-helper扩展了那些功用

retrofit-helper 简洁的封装retrofit,优雅的取消请求

2. 封装逻辑解析

2.1 RetrofitFactory大局办理retrofit实例

DEFAULT 静态变量办理默许常用的的retrofit目标,OTHERS 办理其他多个不同装备的retrofit

/**
 * 创立时刻:2018/4/3
 * 编写人: chengxin
 * 功用描绘:办理大局的Retrofit实例
 */
public final class RetrofitFactory {
    /**
     * 缓存不同装备的retrofit调集,如不同的url ,converter等
     */
    public static final Map<String, Retrofit> OTHERS = new ConcurrentHashMap<>(2);
    /**
     * 大局的Retrofit目标
     */
    public static volatile Retrofit DEFAULT;
private RetrofitFactory() {
}
public static &lt;T&gt; T create(Class&lt;T&gt; service) {
    //确保多线程的情况下retrofit不为空或许被修改了
    Retrofit retrofit = DEFAULT;
    Utils.checkState(retrofit != null, "DEFAULT == null");
    return retrofit.create(service);
}
/**
 * @param name 获取 OTHERS 中指定名字的retrofit
 */
public static &lt;T&gt; T create(String name, Class&lt;T&gt; service) {
    Utils.checkNotNull(name, "name == null");
    Retrofit retrofit = OTHERS.get(name);
    Utils.checkState(retrofit != null,
            String.format("retrofit named with '%s' was not found , have you put it in OTHERS ?", name));
    return retrofit.create(service);
}
}
2.2 Call2接口承继retrofit.Call 重载 enqueue(Callback callback)办法

enqueue(@Nullable Object tag, Callback2 callback2) 办法传入恳求的tag标记此恳求,tag标签就是撤销恳求所需求的

/**
 * 创立时刻:2018/4/8
 * 编写人: chengxin
 * 功用描绘:增加重载办法{@link Call2#enqueue(Object, Callback2)}办法
 */
public interface Call2<T> extends retrofit2.Call<T> {
    /**
     * @param tag       恳求的tag,用于撤销恳求运用
     * @param callback2 恳求的回调
     */
    void enqueue(@Nullable Object tag, Callback2<T> callback2);
@Override
Call2&lt;T&gt; clone();
}
2.3 Callback2 共同处理回调

恳求开端、成功处理、失利处理、成功回调、失利回调、恳求完毕在此共同处理,各办法能够根据业务的不同自行重写,例如:能够重写parseResponse办法根据不通的http code做不同的提示描绘 或许

重写parseThrowable办法处理各种Throwable

@UiThread
public abstract class Callback2<T> {
public abstract void onStart(Call2&lt;T&gt; call2);
@NonNull
public Result&lt;T&gt; parseResponse(Call2&lt;T&gt; call2, Response&lt;T&gt; response) {
    T body = response.body();
    if (response.isSuccessful()) {
        if (body != null) {
            return Result.success(body);
        } else {
            return Result.error(new HttpError("暂无数据", response));
        }
    }
    final String msg;
    switch (response.code()) {
        case 400:
            msg = "参数过错";
            break;
        case 401:
            msg = "身份未授权";
            break;
        case 403:
            msg = "制止拜访";
            break;
        case 404:
            msg = "地址未找到";
            break;
        default:
            msg = "服务反常";
    }
    return Result.error(new HttpError(msg, response));
}
/**
 * 共同解析Throwable目标转换为HttpError目标。假如为HttpError,
 * 则为{@link retrofit2.Converter#convert(Object)}内抛出的反常
 *
 * @param call2 call
 * @param t     Throwable
 * @return HttpError result
 */
@NonNull
public HttpError parseThrowable(Call2&lt;T&gt; call2, Throwable t) {
    if (t instanceof HttpError) {
        //用于convert函数直接抛出反常接纳
        return (HttpError) t;
    } else if (t instanceof UnknownHostException) {
        return new HttpError("网络反常", t);
    } else if (t instanceof ConnectException) {
        return new HttpError("网络反常", t);
    } else if (t instanceof SocketException) {
        return new HttpError("服务反常", t);
    } else if (t instanceof SocketTimeoutException) {
        return new HttpError("响应超时", t);
    } else {
        return new HttpError("恳求失利", t);
    }
}
public abstract void onError(Call2&lt;T&gt; call2, HttpError error);
public abstract void onSuccess(Call2&lt;T&gt; call2, T response);
/**
 * @param t        恳求失利的过错信息
 * @param canceled 恳求是否被撤销了
 */
public abstract void onCompleted(Call2&lt;T&gt; call2, @Nullable Throwable t, boolean canceled);
}
2.4 HttpError 共同处理反常过错

HttpError类中有两个成员特点msg 被body,msg是保存过错的描绘信息等,body能够保存反常的详细信息或许原始的json等,onError(Call2 call2, HttpError error)回调办法能够根据body的详细信息做二次处理。

/**
 * 通用的过错信息,一般恳求是失利只需求弹出一些过错信息即可,like{@link retrofit2.HttpException}
 * Created by chengxin on 2017/6/22.
 */
public final class HttpError extends RuntimeException {
    private static final long serialVersionUID = -134024482758434333L;
    /**
     * 展现在前端的过错描绘信息
     */
    public String msg;
/**
 * &lt;p&gt;
 * 恳求失利保存失利信息,for example:
 * &lt;li&gt;BusiModel: {code:xxx,msg:xxx} 业务过错信息&lt;/li&gt;
 * &lt;li&gt;original json:  原始的json&lt;/li&gt;
 * &lt;li&gt;{@link retrofit2.Response}:过错响应体-&gt;Response&lt;?&gt;&lt;/li&gt;
 * &lt;li&gt;Throwable: 抛出的反常信息&lt;/li&gt;
 * &lt;/p&gt;
 */
@Nullable
public final transient Object body;
public HttpError(String msg) {
    this(msg, null);
}
public HttpError(String msg, @Nullable Object body) {
    super(msg);
    if (body instanceof Throwable) {
        initCause((Throwable) body);
    }
    //FastPrintWriter#print(String str)
    this.msg = msg != null ? msg : "null";
    this.body = body;
}
/**
 * 确保和msg共同
 */
@Override
public String getMessage() {
    return msg;
}
@Override
public String toString() {
    return "HttpError {msg="
            + msg
            + ", body="
            + body
            + '}';
}
}
2.5 ExecutorCallAdapterFactory回来Call2恳求适配器

处理恳求接口办法回来为Call2的恳求适配器工厂类

public final class ExecutorCallAdapterFactory extends CallAdapter.Factory {
public static final CallAdapter.Factory INSTANCE = new ExecutorCallAdapterFactory();
private ExecutorCallAdapterFactory() {
}
/**
 * Extract the raw class type from {@code type}. For example, the type representing
 * {@code List&lt;? extends Runnable&gt;} returns {@code List.class}.
 */
public static Class&lt;?&gt; getRawType(Type type) {
    return CallAdapter.Factory.getRawType(type);
}
@Override
public CallAdapter&lt;?, ?&gt; get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != Call2.class) {
        return null;
    }
    if (!(returnType instanceof ParameterizedType)) {
        throw new IllegalArgumentException(
                "Call return type must be parameterized as Call2&lt;Foo&gt; or Call2&lt;? extends Foo&gt;");
    }
    final Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
    final Executor callbackExecutor = retrofit.callbackExecutor();
    if (callbackExecutor == null) throw new AssertionError();
    return new CallAdapter&lt;Object, Call&lt;?&gt;&gt;() {
        @Override
        public Type responseType() {
            return responseType;
        }
        @Override
        public Call&lt;Object&gt; adapt(Call&lt;Object&gt; call) {
            return new ExecutorCallbackCall2&lt;&gt;(callbackExecutor, call);
        }
    };
}
}
2.6 ExecutorCallbackCall2 承继Call2署理OkHttpCall处理UI回调

装饰者模式署理OkHttpCall的一切办法,线程调度处理 Callback2 的回调办法在主线程履行

final class ExecutorCallbackCall2<T> implements Call2<T> {
    private final Executor callbackExecutor;
    private final Call<T> delegate;
/**
 * The executor used for {@link Callback} methods on a {@link Call}. This may be {@code null},
 * in which case callbacks should be made synchronously on the background thread.
 */
ExecutorCallbackCall2(Executor callbackExecutor, Call&lt;T&gt; delegate) {
    this.callbackExecutor = callbackExecutor;
    this.delegate = delegate;
}
@Override
public void enqueue(final Callback&lt;T&gt; callback) {
    throw new UnsupportedOperationException("please call enqueue(Object tag, Callback2&lt;T&gt; callback2)");
}
@Override
public void enqueue(@Nullable Object tag, final Callback2&lt;T&gt; callback2) {
    Utils.checkNotNull(callback2, "callback2==null");
    CallManager.getInstance().add(this, tag != null ? tag : "NO_TAG");
    callbackExecutor.execute(new Runnable() {
        @Override
        public void run() {
            if (!isCanceled()) {
                callback2.onStart(ExecutorCallbackCall2.this);
            }
        }
    });
    delegate.enqueue(new Callback&lt;T&gt;() {
        @Override
        public void onResponse(Call&lt;T&gt; call, final Response&lt;T&gt; response) {
            callbackExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    callResult(callback2, response, null);
                }
            });
        }
        @Override
        public void onFailure(Call&lt;T&gt; call, final Throwable t) {
            callbackExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    callResult(callback2, null, t);
                }
            });
        }
    });
}
@UiThread
private void callResult(Callback2&lt;T&gt; callback2, @Nullable Response&lt;T&gt; response, @Nullable Throwable failureThrowable) {
    try {
        if (!isCanceled()) {
            //1、获取解析结果
            Result&lt;T&gt; result;
            if (response != null) {
                result = callback2.parseResponse(this, response);
                Utils.checkNotNull(result, "result==null");
            } else {
                Utils.checkNotNull(failureThrowable, "failureThrowable==null");
                HttpError error = callback2.parseThrowable(this, failureThrowable);
                result = Result.error(error);
            }
            //2、回调成功失利
            if (result.isSuccess()) {
                callback2.onSuccess(this, result.body());
            } else {
                callback2.onError(this, result.error());
            }
        }
        callback2.onCompleted(this, failureThrowable, isCanceled());
    } finally {
        CallManager.getInstance().remove(this);
    }
}
@Override
public boolean isExecuted() {
    return delegate.isExecuted();
}
@Override
public Response&lt;T&gt; execute() throws IOException {
    return delegate.execute();
}
@Override
public void cancel() {
    delegate.cancel();
}
@Override
public boolean isCanceled() {
    return delegate.isCanceled();
}
@SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
@Override
public Call2&lt;T&gt; clone() {
    return new ExecutorCallbackCall2&lt;&gt;(callbackExecutor, delegate.clone());
}
@Override
public Request request() {
    return delegate.request();
}
}
2.7 CallManager共同办理恳求,撤销恳求

大局保存一切的恳求,增加 、删去恳求,撤销某个某些匹配tag的恳求。能够在Activity 或Fragment的毁掉办法中调用CallManager.getInstance().cancel( yourTag )

/**
 * 创立时刻:2018/5/31
 * 编写人: chengxin
 * 功用描绘:大局办理Call恳求办理,just like {@link okhttp3.Dispatcher}
 */
public final class CallManager implements ActionManager<Call<?>> {
    @GuardedBy("this")
    private final List<CallTag> callTags = new ArrayList<>(4);
    private volatile static CallManager instance;
private CallManager() {
}
public static CallManager getInstance() {
    if (instance == null) {
        synchronized (CallManager.class) {
            if (instance == null) {
                instance = new CallManager();
            }
        }
    }
    return instance;
}
@Override
public synchronized void add(Call&lt;?&gt; call, Object tag) {
    Utils.checkState(!contains(call), "Call&lt;?&gt;  " + call + " is already added.");
    callTags.add(new CallTag(call, tag));
}
/**
 * 当call完毕时移除
 *
 * @param call Retrofit Call
 */
@Override
public synchronized void remove(Call&lt;?&gt; call) {
    if (callTags.isEmpty())
        return;
    for (int index = 0; index &lt; callTags.size(); index++) {
        if (call == callTags.get(index).call) {
            //like okhttp3.Headers#removeAll(String name)
            //remove(int index) 办法优于 remove(Object o),无需再次遍历
            callTags.remove(index);
            break;
        }
    }
}
/**
 * 撤销并移除对应tag的call,确保Call被撤销后不再被引用,
 * 结合{@link #remove(Call)}办法双保险
 *
 * @param tag call对应的tag
 */
@Override
public synchronized void cancel(final @Nullable Object tag) {
    if (callTags.isEmpty())
        return;
    if (tag != null) {
        for (int index = 0; index &lt; callTags.size(); index++) {
            CallTag callTag = callTags.get(index);
            if (callTag.tag.equals(tag)) {
                callTag.call.cancel();
                callTags.remove(index);
                index--;
            }
        }
    } else {
        for (CallTag callTag : callTags) {
            callTag.call.cancel();
        }
        callTags.clear();
    }
}
@Override
public synchronized boolean contains(Call&lt;?&gt; call) {
    for (CallTag callTag : callTags) {
        if (call == callTag.call) {
            return true;
        }
    }
    return false;
}
/**
 * 保存call和tag
 */
final static class CallTag {
    private final Call&lt;?&gt; call;
    private final Object tag;
    CallTag(Call&lt;?&gt; call, Object tag) {
        Utils.checkNotNull(call == null, "call==null");
        Utils.checkNotNull(tag == null, "tag==null");
        this.call = call;
        this.tag = tag;
    }
}
}
2.8 ProgressInterceptor 拦截器监听下载和上传进展

承继okhttp3.Interceptor ,结构办法中传入ProgressListener监听进展

/**
 * 创立时刻:2018/8/2
 * 编写人: chengxin
 * 功用描绘:上传或下载进展监听拦截器
 */
public class ProgressInterceptor implements Interceptor {
private final ProgressListener mProgressListener;
public ProgressInterceptor(ProgressListener progressListener) {
    Utils.checkNotNull(progressListener, "progressListener==null");
    this.mProgressListener = progressListener;
}
@Override
public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RequestBody requestBody = request.body();
    //判别是否有上传需求
    if (requestBody != null &amp;&amp; requestBody.contentLength() &gt; 0) {
        Request.Builder builder = request.newBuilder();
        RequestBody newRequestBody = new ProgressRequestBody(requestBody, mProgressListener, request);
        request = builder.method(request.method(), newRequestBody).build();
    }
    Response response = chain.proceed(request);
    ResponseBody responseBody = response.body();
    if (responseBody != null &amp;&amp; responseBody.contentLength() &gt; 0) {
        Response.Builder builder = response.newBuilder();
        ResponseBody newResponseBody = new ProgressResponseBody(responseBody, mProgressListener, request);
        response = builder.body(newResponseBody).build();
    }
    return response;
}
}

2.9 HttpLoggingInterceptor 能够单独指定某个恳求的日志等级

结构OkhttpClient时增加此拦截器,在恳求的服务办法中增加注解

@Headers(“LogLevel:NONE”) 或 @Headers(“LogLevel:BASIC”) 或 @Headers(“LogLevel:HEADERS”) 或@Headers(“LogLevel:BODY”)

@FormUrlEncoded
@Headers("LogLevel:HEADERS")
@POST("user/login")
Call2<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);

3.实战

3.1 初始化大局Retrofit目标
Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://wanandroid.com/")
                .callFactory(new OkHttpClient.Builder()
                        .addNetworkInterceptor(httpLoggingInterceptor)
                        .build())
                //必须增加此adapter 用于构建处理回调
                .addCallAdapterFactory(ExecutorCallAdapterFactory.INSTANCE)
                //增加自定义json解析器 
                .addConverterFactory(GsonConverterFactory.create())
                .build();
 RetrofitFactory.DEFAULT = retrofit;
 //能够增加多个,如:
 RetrofitFactory.OTHERS.put("other",otherRetrofit);
3.2 增加恳求服务接口

下面为登录的 post恳求

@FormUrlEncoded
@POST("user/login")
Call2<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);
3.3 增加ILoadingView,用于敞开和完毕动画

Activity 或许Fragment 能够承继 ILoadingView接口完成开端和完毕动画

public interface ILoadingView {
    /**
     * 显示加载
     */
    void showLoading();
/**
 * 躲藏加载
 */
void hideLoading();
}
3.4 增加AnimCallback 处理动画

这儿重写parseThrowable处理一些Callback2中为未处理的反常

public abstract class AnimCallback<T> extends Callback2<T> {
    private ILoadingView mLoadingView;
public AnimCallback(@Nullable ILoadingView loadingView) {
    this.mLoadingView = loadingView;
}
@Override
public void onStart(Call2&lt;T&gt; call2) {
    if (mLoadingView != null)
        mLoadingView.showLoading();
}
@Override
public void onCompleted(Call2&lt;T&gt; call2, @Nullable Throwable t, boolean canceled) {
    if (canceled)
        return;
    if (mLoadingView != null)
        mLoadingView.hideLoading();
}
@NonNull
@Override
public HttpError parseThrowable(Call2&lt;T&gt; call2, Throwable t) {
    HttpError filterError;
    if (t instanceof JsonSyntaxException) {
        filterError = new HttpError("解析反常", t);
    } else {
        filterError = super.parseThrowable(call2, t);
    }
    return filterError;
}
}
3.5 发起恳求
RetrofitFactory.create(ApiService.class)
        .getLogin("xxxxx", "123456")
        .enqueue(hashCode(), new AnimCallback<LoginInfo>(this) {
            @Override
            public void onError(Call2<LoginInfo> call2, HttpError error) {
                //处理失利
            }
        @Override
        public void onSuccess(Call2&lt;LoginInfo&gt; call2, LoginInfo response) {
           //处理成功 如保存登录信息等
        }
    });
 //在onDestor中撤销未完毕的恳求
   @Override
    protected void onDestroy() {
        super.onDestroy();
        //hashCode() 能确保唯一性,撤销当前页面所发起的一切恳求,只需
        // enqueue(tag, callback2) 传入的是对应的hashCode() 即可
        CallManager.getInstance().cancel(hashCode());
    }

4.注意事项

4.1 构建retrofit是需求ExecutorCallAdapterFactory实例,不然无法处理回来为Call2的服务接口
4.2 Callback2的回调函数均在主线程履行,假如调用了Call2.cancel()办法,除了onCompleted()办法会履行外其他回调办法都不会履行

5.下载

implementation "com.xcheng:retrofit-helper:1.0.0"\
Copyright 2019 xchengDroid
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

更多Android进阶攻略 能够详细Vx关注大众号:Android老皮 解锁 《Android十大板块文档》

1.Android车载使用开发体系学习攻略(附项目实战)

2.Android Framework学习攻略,助力成为体系级开发高手

3.2023最新Android中高档面试题汇总+解析,离别零offer

4.企业级Android音视频开发学习道路+项目实战(附源码

5.Android Jetpack从入门到通晓,构建高质量UI界面

6.Flutter技术解析与实战,跨渠道首要之选

7.Kotlin从入门到实战,全方面提高架构根底

8.高档Android插件化与组件化(含实战教程和源码)

9.Android 性能优化实战+360全方面性能调优

10.Android零根底入门到通晓,高手进阶之路

敲代码不易,关注一下吧。ღ( ・ᴗ・` )