再见 Retrofit(实战)
上篇简单介绍了 Retrofit 的使用,初探 Retrofit(入门)https://www.jianshu.com/p/8dc7cc4d0339,不了解的可以先看看。这期主要想聊聊具体在项目中的使用。
先来说说实际情况,实际项目中我们的请求返回体一般会有固定格式,例如:
public class SampleResp {
public int code; //业务码
public String msg; //提示信息
public String data; //接口返回数据
}
在 response.code() 为 200 的基础上,在根据 SampleResp.code 来进行业务处理。还有一种返回体格式是少了一层包装的,是直接根据 response.code() 的返回码进行判断的,根据 code 码再去取 response.body() 里的数据,这种格式我觉得其实不太规范,混淆了网络请求的 code 码和业务 code 码。但实际应用上是没问题的,目前项目中两种情况都有在用。
接下来解析返回数据,肯定会有一些是要做通用处理的,例如 token 失效,通用的错误提示等。如果每写一个请求就写一遍这些处理,不光写的烦,后期改动也麻烦。
另外一个需要改进的地方是,我们的请求方法都写在一个 interface 里,每调用一个请求都要用 retrofit 实例去创建 interface 实例,最后发起请求,那这部分也可以简化一下,我想采用单例模式,一次创建多次使用。
下面我们就开始。
RetrofitManager
新建一个管理类,来处理 retrofit,service,以及一些配置。大致的思路是,配置好 retrofit 并生成 service 服务,再根据具体操作去调取服务的相关方法就好了。
public class SampleRetrofitManager {
private ApiServiceFactory service;
private static class RetrofitManagerHolder {
public static SampleRetrofitManager INSTANCE = new SampleRetrofitManager();
}
private SampleRetrofitManager() {
OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
httpBuilder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
//要是需要添加请求头可以在这里
Request original = chain.request();
Request.Builder reqBuilder = original.newBuilder()
.header("header1", "header1")
.method(original.method(), original.body());
reqBuilder.addHeader("header2", "header2");
return chain.proceed(reqBuilder.build());
}
});
//添加拦截器,可以将请求,返回的日志打印出来
httpBuilder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
//时间配置
httpBuilder.connectTimeout(15, TimeUnit.SECONDS);
httpBuilder.readTimeout(20, TimeUnit.SECONDS);
httpBuilder.writeTimeout(20, TimeUnit.SECONDS);
//这好像还感觉不出怎么用,我看网上这么写,先写着还没测
httpBuilder.retryOnConnectionFailure(true);
//这里就是 retrofit 和 service 配置
Retrofit.Builder reBuilder = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = reBuilder.client(httpBuilder.build())
.baseUrl(BuildConfig.APP_HTTP_URL).build();
service = retrofit.create(ApiServiceFactory.class);
}
public static SampleRetrofitManager getInstance() {
return RetrofitManagerHolder.INSTANCE;
}
}
这里遇到一个实际情况,就是服务器地址有可能不止一个,我的处理是再添加一个 service 变量,再配置一下地址就好了,用到服务 A 的就调 service A ,用到服务 B 的就调 service B。
到这里我们实现了用单例模式改进了 service 服务的调用,接着我们对请求返回做下封装,来帮我们做一些统一处理。
FormalCallback
public abstract class FormalCallback<T> implements Callback<SampleResp>{
private Class<T> responseClass;
public void setRespClass(Class<T> respClass) {
responseClass = respClass;
}
@Override
public void onResponse(Call<SampleResp> call, Response<SampleResp> response) {
if (200 == response.code() && null != response.body()) {
SampleResp resp = response.body();
if (201 == resp.code) {
// token 失效处理
} else if (200 == resp.code) {
T body = GsonUtil.getInstance().fromJson(resp.data, responseClass);
onSuccess(body);
} else {
onFail();
}
}
}
@Override
public void onFailure(Call<SampleResp> call, Throwable t) {
onFail();
}
public abstract void onSuccess(T model);
public abstract void onFail();
}
上面是针对有格式化返回体的异步请求的封装,实现了 retrofit 的 Callback 接口,在 onResponse 里对结果做了处理,并通过实现 onSuccess 和 onFail 来进一步处理。在使用的时候就更加集中处理业务结果。最后来说说非格式化返回体的封装。
InFormalCallback
public abstract class InFormalCallback<T> implements Callback<ResponseBody> {
private Class<T> responseClass;
public void setRespClass(Class<T> responseClass) {
this.responseClass = responseClass;
}
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
if (response.code() == 200 && response.body() != null) {
T body = GsonUtil.getInstance().fromJson(response.body().string(), responseClass);
onSuccess(body);
} else if (response.code() == 201) {
//token 失效
} else {
ErrorResponse error = GsonUtil.getInstance().fromJson(response.errorBody().string(), ErrorResponse.class);
onFail(error);
}
}catch (Exception e) {
e.printStackTrace();
onFail(new ErrorResponse());
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
onFail(new ErrorResponse());
}
public abstract void onSuccess(T model);
public abstract void onFail(ErrorResponse error);
}
非格式化和格式化的返回体封装相比,只是解析数据上略有不同,其他处理上基本一样。
到这里我们就对请求返回的统一处理就做好了,文章一开始提到的两个需要改进的地方也就处理好了。现在把上面这些用起来是没问题的,但我觉得还是可以再封装一下,于是我又做了一步。
public class SampleServiceCall<T> {
//格式化返回体的 post 请求方法
public void formalPost(Call<SampleResp> call, Class<T> responseClass, FormalCallback<T> callback) {
if (null == call || null == responseClass) {
return;
}
callback.setRespClass(responseClass);
call.enqueue(callback);
}
}
其实就是多调了一个自定义方法,为了统一处理,就再用方法包装了一下。我只写了格式化返回体的请求方法,非格式化的类似。为了写代码的时候更加集中到业务上,建议可以对格式化请求体做一下方法封装。
好了,上面这些我觉得是可以用到实际项目中的,这里只用了 retrofit,但大家知道 retrofit 一般都和 rxjava 混在一起,那么下期就来讲讲他们是怎么勾搭在一起的。