Retrofit是很多android开发者都在使用的Http请求库!他负责网络请求接口的封装,底层实现是OkHttp,它的一个特点是包含了特别多注解,方便简化你的代码量,CallAdapter.Factory 和Converter.Factory可以很灵活的扩展你的请求。我们在使用的时候还是需要封装一层便于我们使用,retrofit-helper的作用就是再次简化你的请求。
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 <T> T create(Class<T> service) {
//确保多线程的情况下retrofit不为空或者被修改了
Retrofit retrofit = DEFAULT;
Utils.checkState(retrofit != null, "DEFAULT == null");
return retrofit.create(service);
}
/**
* @param name 获取 OTHERS 中指定名字的retrofit
*/
public static <T> T create(String name, Class<T> 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);
}
}
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<T> clone();
}
请求开始、成功处理、失败处理、成功回调、失败回调、请求结束在此统一处理,各方法可以根据业务的不同自行重写,例如:可以重写parseResponse方法根据不通的http code做不同的提示描述 或者
重写parseThrowable方法处理各种Throwable
@UiThread
public abstract class Callback2<T> {
public abstract void onStart(Call2<T> call2);
@NonNull
public Result<T> parseResponse(Call2<T> call2, Response<T> 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<T> 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<T> call2, HttpError error);
public abstract void onSuccess(Call2<T> call2, T response);
/**
* @param t 请求失败的错误信息
* @param canceled 请求是否被取消了
*/
public abstract void onCompleted(Call2<T> call2, @Nullable Throwable t, boolean canceled);
}
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;
/**
* <p>
* 请求失败保存失败信息,for example:
* <li>BusiModel: {code:xxx,msg:xxx} 业务错误信息</li>
* <li>original json: 原始的json</li>
* <li>{@link retrofit2.Response}:错误响应体->Response<?></li>
* <li>Throwable: 抛出的异常信息</li>
* </p>
*/
@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
+ '}';
}
}
处理请求接口方法返回为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<? extends Runnable>} returns {@code List.class}.
*/
public static Class<?> getRawType(Type type) {
return CallAdapter.Factory.getRawType(type);
}
@Override
public CallAdapter<?, ?> 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<Foo> or Call2<? extends Foo>");
}
final Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor callbackExecutor = retrofit.callbackExecutor();
if (callbackExecutor == null) throw new AssertionError();
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return new ExecutorCallbackCall2<>(callbackExecutor, call);
}
};
}
}
装饰者模式代理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<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override
public void enqueue(final Callback<T> callback) {
throw new UnsupportedOperationException("please call enqueue(Object tag, Callback2<T> callback2)");
}
@Override
public void enqueue(@Nullable Object tag, final Callback2<T> 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<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(new Runnable() {
@Override
public void run() {
callResult(callback2, response, null);
}
});
}
@Override
public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(new Runnable() {
@Override
public void run() {
callResult(callback2, null, t);
}
});
}
});
}
@UiThread
private void callResult(Callback2<T> callback2, @Nullable Response<T> response, @Nullable Throwable failureThrowable) {
try {
if (!isCanceled()) {
//1、获取解析结果
Result<T> 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<T> 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<T> clone() {
return new ExecutorCallbackCall2<>(callbackExecutor, delegate.clone());
}
@Override
public Request request() {
return delegate.request();
}
}
全局保存所有的请求,添加 、删除请求,取消某个某些匹配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<?> call, Object tag) {
Utils.checkState(!contains(call), "Call<?> " + call + " is already added.");
callTags.add(new CallTag(call, tag));
}
/**
* 当call结束时移除
*
* @param call Retrofit Call
*/
@Override
public synchronized void remove(Call<?> call) {
if (callTags.isEmpty())
return;
for (int index = 0; index < 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 < 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<?> call) {
for (CallTag callTag : callTags) {
if (call == callTag.call) {
return true;
}
}
return false;
}
/**
* 保存call和tag
*/
final static class CallTag {
private final Call<?> call;
private final Object tag;
CallTag(Call<?> call, Object tag) {
Utils.checkNotNull(call == null, "call==null");
Utils.checkNotNull(tag == null, "tag==null");
this.call = call;
this.tag = tag;
}
}
}
继承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 && requestBody.contentLength() > 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 && responseBody.contentLength() > 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);
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);
下面为登录的 post请求
@FormUrlEncoded
@POST("user/login")
Call2<LoginInfo> getLogin(@Field("username") String username, @Field("password") String password);
Activity 或者Fragment 可以继承 ILoadingView接口实现开始和结束动画
public interface ILoadingView {
/**
* 显示加载
*/
void showLoading();
/**
* 隐藏加载
*/
void hideLoading();
}
这里重写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<T> call2) {
if (mLoadingView != null)
mLoadingView.showLoading();
}
@Override
public void onCompleted(Call2<T> call2, @Nullable Throwable t, boolean canceled) {
if (canceled)
return;
if (mLoadingView != null)
mLoadingView.hideLoading();
}
@NonNull
@Override
public HttpError parseThrowable(Call2<T> call2, Throwable t) {
HttpError filterError;
if (t instanceof JsonSyntaxException) {
filterError = new HttpError("解析异常", t);
} else {
filterError = super.parseThrowable(call2, t);
}
return filterError;
}
}
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<LoginInfo> call2, LoginInfo response) {
//处理成功 如保存登录信息等
}
});
//在onDestor中取消未结束的请求
@Override
protected void onDestroy() {
super.onDestroy();
//hashCode() 能保证唯一性,取消当前页面所发起的所有请求,只要
// enqueue(tag, callback2) 传入的是对应的hashCode() 即可
CallManager.getInstance().cancel(hashCode());
}
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进阶指南 可以扫码 解锁 《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零基础入门到精通,高手进阶之路
敲代码不易,关注一下吧。ღ( ´・ᴗ・` )