前言
Retrofit的使用已近有一段时间了,并做了一些简单的封装,但仅仅会使用是远远不够的,为此希望做一个总结,1是对Retroft的封装和使用、2是Retroft涉及到的Java技术、3是Retrofit相关的源码分析。这一篇主要是记录Retrofit的封装使用。
对于一个网络框架,在使用中我希望能做如下封装
- 统一无网络提示、统一网络超时判断
- 添加统一Heard头,添加统一请求参数
- 设置数据请求缓存
- 统一数据解析
- 统一处理Http请求错误如Code等于500,统一处理后台返回的errorCode,如token过期错误
- 统一请求数据加密
接下来开始实现上面的功能,首先对:统一无网络提示、统一网络超时判断
实现这个需要用到OkHttp的拦截器, 在下面HttpClient工具类中设置网络拦截器,拦截器中返回的是一个Chain接口,Chain接口是OkHttp中Interceptor的一个接口
public class NetWorkInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
if (!NetWorkUtils.isNetWorkAvailable(BaseApplication.getInstance())) {
throw new SocketException("网络未连接!");
} else {
return chain.proceed(chain.request());
}
}
}
可以根据下面方法获取到
Request和
Response, Request.Builder builder = originalRequest.newBuilder()
;就返回一个Request.Builder,Request.Builder中可以设置有关Header相关的参数。
Request request = chain.request();
Response response = chain.proceed(request);
看下面拦截器的相关方法可知道在其中可以设置相关超时时间,具体拦截中能做什么其实就看拦截器中能获取到那些参数,那么其相关的的属性就可以进行设置
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
添加统一Heard头,添加统一请求参数:
public class QueryParameterInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
originalRequest.newBuilder()
.addHeader("version", "1")
.addHeader("time", System.currentTimeMillis() + "");
HttpUrl.Builder authorizedUrlBuilder = originalRequest.url()
.newBuilder()
.scheme(originalRequest.url().scheme())//设置Http或Https
.host(originalRequest.url().host())//设置host
.addQueryParameter("aaa", "111")
.addQueryParameter("bbb", "222");
Log.i("TAG", "=======" + originalRequest.body().toString());
Request newRequest = originalRequest.newBuilder()
.method(originalRequest.method(), originalRequest.body())
.url(authorizedUrlBuilder.build())
.build();
return chain.proceed(newRequest);
}
}
设置数据请求缓存
public class CacheInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetWorkUtils.isNetWorkAvailable(BaseApplication.getInstance())) {//没网强制从缓存读取(必须得写,不然断网状态下,退出应用,或者等待一分钟后,就获取不到缓存)
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (NetWorkUtils.isNetWorkAvailable(BaseApplication.getInstance())) {//有网情况下,从服务器获取
int maxAge = BuildConfigs.DEFAULT_COOKIE_NETWORK_TIME;
// 有网络时, 缓存最大保存时长为60s
response.newBuilder()
.header("Cache-Control", "public, max-age=" + maxAge)
.removeHeader("Pragma")
.build();
} else {//没网情况下,一律从缓存获取
// 无网络时,设置超时为30天
int maxStale = BuildConfigs.DEFAULT_COOKIE_NO_NETWORK_TIME;
response.newBuilder()
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.removeHeader("Pragma")
.build();
}
return response;
}
}
统一数据解析,MyGsonConverterFactory的使用是在Retrofit创建的时候进行设置,Converter是Retrofit对数据解析的封装,
public final class MyGsonConverterFactory extends Converter.Factory {
/**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static MyGsonConverterFactory create() {
return create(new Gson());
}
/**
* Create an instance using {@code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static MyGsonConverterFactory create(Gson gson) {
return new MyGsonConverterFactory(gson);
}
private final Gson gson;
private MyGsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new ResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new RequestBodyConverter<>(gson, adapter);
}
}
ResponseBodyConverter的convert方法会得到ResponseBody,这个类是我们自己定义的,一般定义方法就是一个errorCode,一个errorMsg,以及一个泛型对象,泛型对象就是我们具体要得到的对象。在请求成功的情况下, 我们可以将所有非正常返回在这里进行一次过滤,但是一些请求失败的并不能在这里进行设置提示。需要我们自定义Callback。
public final class ResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private static final int SUCCESS = 0; // 成功
private static final int FAILURE = 1; // 失败 提示失败msg
private static final int TOKEN_EXPIRE = 2; // token过期
private static final int SERVER_EXCEPTION = -1; // 服务器异常
private final Gson gson;
private final TypeAdapter<T> adapter;
public ResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override
public T convert(ResponseBody value) throws IOException {
String json = value.string();
verify(json);
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(json).getJSONObject("data");
} catch (JSONException e) {
e.printStackTrace();
}
try {
return adapter.read(gson.newJsonReader(new StringReader(jsonObject.toString())));
} finally {
value.close();
}
}
private void verify(String json) {
ResponseData result = gson.fromJson(json, ResponseData.class);
if (result.getErrorCode() != SUCCESS) {
switch (result.getErrorCode()) {
case FAILURE:
case SERVER_EXCEPTION:
throw new MyException(result.getErrorMsg());
case TOKEN_EXPIRE:
throw new MyException(result.getErrorMsg());
default:
throw new MyException(result.getErrorMsg());
}
}
}
}
对于统一的加密,这里首先要获取到设置的所有参数,参数都存在于RequestBody中,我们可以在RequestBodyConverter的convert中获取到这个参数,但是,RequestBodyConverter的convert方法,并不是所有的请求都会被调用,目前我知道的是当设置参数为@Body
的时候时是可以调用到的,具体的什么时候被调用到得看RequestFactory
类中哪里调用了retrofit.requestBodyConverter
方法
public final class RequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType
.parse("application/json; charset=UTF-8");
static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter<T> adapter;
RequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
System.out.println("#IRequestBodyConverter初始化#");
}
@Override
public RequestBody convert(T value) throws IOException {
String json = value.toString();
Log.e("LOG", "#加密前#" + json);
// json = AesEncryptionUtil.encrypt(json);
Log.e("LOG", "#加加密后#" + json);
return RequestBody.create(MEDIA_TYPE, json);
}
}
在这里我们创建一个自定义Callback,在此处可以处理请求过程中response返回的400,500等提示,并且也可以在这里处理ResponseData中不同Code的结果
public abstract class OkCallback<T> implements Callback<T> {
@Override
public void onResponse(Call<T> call, Response<T> response) {
//这里可以对其他Code 吗码进行监听,做特殊处理
if (200 == response.code()) {
onSuccessful(call, response);
} else {
Throwable throwable = new Throwable(response.message());
onFail(call, throwable, response);
}
}
public abstract void onSuccessful(Call<T> call, Response<T> response);
@Override
public void onFailure(Call<T> call, Throwable t) {
onFail(call, t, null);
}
protected void onFail(Call<T> call, Throwable t, Response<T> response) {
Toast.makeText(BaseApplication.getInstance(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
}
public class ResponseData<T> implements Serializable {
private static final long serialVersionUID = 5213230387175987834L;
private int errorCode;
private String errorMsg;
private T data;
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
这里是对前面添加的设置统一处理,请求前的封装
public class HttpClient {
private Api api;
private HttpClient() {
Cache cache = new Cache(new File(BuildConfigs.PATH_CACHE), BuildConfigs.DEFAULT_CACHE_SIZE);
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(new NetWorkInterceptor())
.addInterceptor(new HeaderInterceptor())//设置Header
.addInterceptor(new QueryParameterInterceptor())//设置请求参数
.addNetworkInterceptor(new CacheNetWorkInterceptor())//设置缓存
.addInterceptor(new CacheInterceptor())
.cache(cache)
.connectTimeout(BuildConfigs.DEFAULT_CONNECT_TIMEOUT, TimeUnit.SECONDS)//设置连接超时时间
.readTimeout(BuildConfigs.DEFAULT_READ_TIMEOUT, TimeUnit.SECONDS)//设置读取超时时间
.writeTimeout(BuildConfigs.DEFAULT_WRITE_TIMEOUT, TimeUnit.SECONDS)//设置写的超时时间
.retryOnConnectionFailure(true);//错误重连
// okhttp3.internal.Util.checkDuration();
//调试模式打印Log日志
if (BuildConfig.DEBUG) {
// builder.addInterceptor(new LoggingInterceptor());
}
OkHttpClient client = builder.build();
Gson gson = new GsonBuilder()
//配置你的Gson
.setDateFormat("yyyy-MM-dd hh:mm:ss")
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(AppConst.BASE_URL)
.client(client)
// .addConverterFactory(GsonConverterFactory.create(gson))
// .addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(MyGsonConverterFactory.create())
// .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
// .callbackExecutor()
.build()
api = retrofit.create(Api.class);
}
private static class HttpClientHolder {
private static final HttpClient INSTANCE = new HttpClient();
}
public static HttpClient getInstance() {
return HttpClientHolder.INSTANCE;
}
public Api getApiService() {
return api;
}
}
private void onLogin() {
HttpClient.getInstance().getApiService()
.onLogin("18201883983", "1111111", "1111111")
.enqueue(new OkCallback<News>() {
@Override
public void onSuccessful(Call<News> call, Response<News> response) {
Toast.makeText(AModuleActivity.this, "登录成功:" + response.body().getUsername(), Toast.LENGTH_SHORT).show();
}
@Override
protected void onFail(Call<News> call, Throwable t, Response<News> response) {
super.onFail(call, t, response);
}
});
}
demo下载地址
Retrofit封装