Retrofit的封装优化
2018-10-10 本文已影响0人
饭勺
一、前言
Retrofit,是针对OKhttp的二次封装,使用在网络请求方面。目前已比较普及。本文通过对Retrofit的封装,让Http请求模板化并且丰富其功能。
二、功能
封装后,功能特性如下:
1.支持网络请求和Activity生命周期绑定;
2.支持主动取消一个请求;
3.支持回调函数的线程选择;
4.支持超时时间配置
5.支持缓存策略配置(包括离线缓存和在线缓存);
6.支持多文件上传和图文上传。
7.上层接口支持多种数据类型设置。
三、使用介绍
1.Http初始化,建议放在Application中:
HttpClient.getInstance().init(context);
2.Builder类介绍
1) CommonBuilder,普通Builder
2) LifeCycleBuilder,继承自CommonBuilder,提供生命周期管理的Builder。
3) UploadBuilder,继承自LifeCycleBuilder,文件上传Builder.
3.Http请求流程和说明
1) 针对每一个url的请求,在对应的请求结果Bean中构建对应Builder。示例如下,在AccountExistResponse构建Builder
public class AccountExistResponse {
public String code;
public String data;
public String message;
@Override
public String toString() {
return "code = " + code +
"; data = " + data +
"; message = " + message;
}
public static class Builder extends CommonBuilder<AccountExistResponse> {
@Override
protected String getPath() {
return "checkAccountIfExist";
}
@Override
protected String getBaseUrl() {
return "https://61.141.236.30:443/api/register/";
}
@Override
protected String getMethod() {
return HttpMethod.POST;
}
}
}
2) 构建参数并发起请求
HashMap<String, String> map = new HashMap();
map.put("phone", "15820463555");
new AccountExistResponse.Builder().addBodyMap(map)
.build(new HttpCallBack<AccountExistResponse>() {
@Override
public void onSuccess(AccountExistResponse accountExistResponse) {
Toast.makeText(HttpDemoActivity.this, "请求成功! " + accountExistResponse.toString(), Toast.LENGTH_LONG).show();
Log.d(TAG, accountExistResponse.toString());
}
@Override
public void onError(int stateCode, String errorInfo) {
Toast.makeText(HttpDemoActivity.this, "请求失败! stateCode = " + stateCode + "; errorInfo = " +errorInfo, Toast.LENGTH_LONG).show();
Log.d(TAG, "stateCode = " + stateCode + "; errorInfo = " +errorInfo);
}
});
3) 默认配置中有超时配置,自定义操作如下:
HttpConfig httpConfig = HttpConfig.create(false);
httpConfig.connectTimeout(5);
httpConfig.readTimeout(5);
httpConfig.writeTimeout(5);
new RouteInfoResponse.Builder().addBodyMap(map)
.addHeader("name", "wsh")
.setHttpCustomConfig(httpConfig)
.build(new HttpCallBack<RouteInfoResponse>() {
@Override
public void onSuccess(RouteInfoResponse RouteInfoResponse) {
Toast.makeText(HttpDemoActivity.this, "请求成功! " + RouteInfoResponse.toString(), Toast.LENGTH_LONG).show();
Log.d(TAG, RouteInfoResponse.toString());
}
@Override
public void onError(int stateCode, String errorInfo) {
Toast.makeText(HttpDemoActivity.this, "请求失败! stateCode = " + stateCode + "; errorInfo = " +errorInfo, Toast.LENGTH_LONG).show();
Log.d(TAG, "stateCode = " + stateCode + "; errorInfo = " +errorInfo);
}
});
a. false 表示:无需Header中默认的配置,注意超时配置不属于Header配置; true表示保留默认的Header信息;
b. 缓存策略也在HttpConfig中配置
4) 生命周期绑定
new WeatherResponse.Builder().setTag(HttpDemoActivity.this) .addParamsMap(map)
//setTag(HttpDemoActivity.this) 即表示此请求会在HttpDemoActivity销毁时自动断开
5) 回调线程设置,根据build的重载方法设置
new RouteInfoResponse.Builder().addBodyMap(map)
.addHeader("name", "wsh")
.build(true, new HttpCallBack<RouteInfoResponse>()
//ture 表示在主线程中返回,默认也是主线程,false即为在子线程中返回
6) 主动取消请求:保存Builder,调用builder.dispose();
四、主要实现流程:HttpClient类的分析
1.HttpClient为单例
2.提供init方法传入context
3.提供refresh方法更新Retrofit对象以及OkHttpClient对象
/**
* @param url
* @param headerConfig
* @return
*/
public HttpClient refresh(String url, HttpConfig headerConfig, HttpCallBack<T> callback) {
//获取缓存Retrofit
if (TextUtils.isEmpty(url)) {
return null;
}
int key = getHashKey(url, headerConfig);
mCurrentRetrofit = getCacheRetrofit(key);
if (mCurrentRetrofit != null) {
mCurrentServices = mCurrentRetrofit.create(HttpServices.class);
return this;
}
OkHttpClient client = getOkHttpClient(url, headerConfig, callback);
try {
mCurrentRetrofit = new Retrofit.Builder()
.client(client)
.addConverterFactory(new ConvertFactory(mGson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(url)
.build();
} catch (Exception e) {
mCurrentRetrofit = null;
e.printStackTrace();
return null;
}
mCurrentServices = mCurrentRetrofit.create(HttpServices.class);
putCacheRetrofit(key, mCurrentRetrofit);
return this;
}
A.Retrofit对象针对key值有缓存,缓存大小为100;
B.Key值的生成由url和header信息共同决定
private OkHttpClient getOkHttpClient(String url, HttpConfig headerConfig, final HttpCallBack<T> callback) {
if (mContext == null) {
return null;
}
if (headerConfig == null) {
headerConfig = HttpConfig.create(true);
}
final HttpConfig config = headerConfig;
//缓存容量
//缓存路径
String cacheFile = mContext.getCacheDir() + "/retrofit";
Cache cache = new Cache(new File(cacheFile), HttpConstants.SIZE_OF_CACHE);
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(config.getConnectTimeout(), TimeUnit.SECONDS)
.readTimeout(config.getReadTimeout(), TimeUnit.SECONDS)
.writeTimeout(config.getWriteTimeout(), TimeUnit.SECONDS)
.addInterceptor(getHttpInterceptor(headerConfig, callback))
.cache(cache);
//测试用 跳过所有认证
if (url.startsWith(HttpConstants.HTTPS)) {
//SSLSocketFactory sslSocketFactory = new SslContextFactory().getSslSocket(mContext).getSocketFactory();
//builder.sslSocketFactory(sslSocketFactory);
builder.sslSocketFactory(new SslContextFactory().createSSLSocketFactory())
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
}
return builder.build();
}
A.针对Https的请求需要加载认证证书,目前没有则跳过所有认证
4.通过getHttpInterceptor方法获取拦截器
private Interceptor getHttpInterceptor(final HttpConfig config, final HttpCallBack<T> callback) {
Interceptor interceptor = new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
Request okHttpRequest = chain.request();
RequestBody body = okHttpRequest.body();
if (body != null) {
Log.i(TAG, "upload intercept: " + body.getClass().getSimpleName());
ProgressRequestBody prb = new ProgressRequestBody(body);
prb.setProgressListener(callback);
Request.Builder builder = okHttpRequest.newBuilder().method(okHttpRequest.method(), prb);
if (!config.getHeaders().isEmpty()) {
Set<Map.Entry<String, String>> entrySet = config.getHeaders().entrySet();
for (Map.Entry<String, String> entry : entrySet) {
builder.addHeader(entry.getKey(), entry.getValue());
}
}
boolean networkAvailable = NetWorkUtils.isNetworkAvailable(mContext);
if (networkAvailable && config.isNeedNetWorkCache()) { //有网络连接,看是否有配置,没配置,则走默认不缓存
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(config.getNetWorkCacheTimeout(), TimeUnit.SECONDS)
.build();
builder.addHeader(HttpConstants.CACHE_CONTROL, cacheControl.toString());
} else if (!networkAvailable && config.isNeedNoNetWorkCache()) { //离线缓存
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(config.getNoNetWorkCacheTimeout(), TimeUnit.SECONDS)
.build();
builder.addHeader(HttpConstants.CACHE_CONTROL, cacheControl.toString());
}
okHttpRequest = builder.build();
}
return chain.proceed(okHttpRequest);
}
};
return interceptor;
}
A. 拦截器中增加Header信息,网络缓存配置,还有设置进度监听等操作。
5.真正的请求方法doSubscribe()
private void doSubscribe(final String pathKey, final int tagHash,
Observable<Response<String>> observable,
final boolean onUiCallBack, final HttpCallBack<T> callback) {
Observable<Pair<String, T>> mapObservable =
observable.map(new Function<Response<String>, Pair<String, T>>() {
@Override
public Pair<String, T> apply(retrofit2.Response<String> response) throws Exception {
String msg = "";
Pair<String, T> pair;
if(response.isSuccessful()){
String data = response.body();
if (data != null) {
Class<T> cls = getParameterizedTypeClass(callback);
T t = mGson.fromJson(data, cls);
if (t != null) {
pair = new Pair<>(data, t);
} else {
msg = "json parse fail";
pair = new Pair<>(msg, null);
}
if (!onUiCallBack && t != null){ //子线程返回
callback.onSuccess(t);
}else if (!onUiCallBack && t == null){
callback.onError(HttpStateCode.ERROR_SUBSCRIBE_ERROR, msg);
}
return pair;
} else {
msg = "response body is null";
}
}else {
ResponseBody errorBody = response.errorBody();
if (errorBody == null) {
msg = "errorBody is null";
} else {
msg = errorBody.string();
}
}
Log.e(TAG, "apply: " + msg);
pair = new Pair<>(msg, null);
if (!onUiCallBack){
callback.onError(HttpStateCode.ERROR_SUBSCRIBE_ERROR, msg);
}
return pair;
}
});
mapObservable.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Pair<String, T>>() {
@Override
public void onSubscribe(Disposable disposable) {
cacheDisposableIfNeed(disposable, tagHash, pathKey);
}
@Override
public void onNext(Pair<String, T> pair) {
if (!onUiCallBack) {
return;
}
T t = pair.second;
if (t != null) {
callback.onSuccess(t);
} else {
callback.onError(HttpStateCode.ERROR_ONNEXT_NULL, null);
}
}
@Override
public void onError(Throwable throwable) {
dispose(tagHash, pathKey);
if (onUiCallBack) {
callback.onError(HttpStateCode.ERROR_SUBSCRIBE_ERROR, throwable == null
? "" : throwable.getMessage());
}
}
@Override
public void onComplete() {
dispose(tagHash, pathKey);
}
});
}
A. 将(retrofit2.Response<String> response)转化为Pair<String, T> ,T即为返回信息的java Bean类;
B. 转为Java Bean类是从callback中的泛型获取到T的class对象,在用Gson转化
private Class<T> getParameterizedTypeClass(Object obj) {
ParameterizedType pt = (ParameterizedType) obj.getClass().getGenericSuperclass();
Type[] atr = pt.getActualTypeArguments();
if (atr != null && atr.length > 0) {
return (Class<T>) atr[0];
}
return null;
}
C.控制返回结果的线程
6.缓存Disposable以便取消请求
/**
* 缓存Disposable
*
* @param disposable
* @param hash
* @param key
*/
private void cacheDisposableIfNeed(Disposable disposable, int hash, String key) {
if (disposable == null) {
return;
}
Pair<Integer, Disposable> pair = Pair.create(key.hashCode(), disposable);
List<Pair<Integer, Disposable>> list = mDisposableCache.get(hash);
if (list == null) {
list = new ArrayList<>();
}
list.add(pair);
mDisposableCache.put(hash, list);
}
7.取消请求的实现
/**
* @param tag 请求时传入的tag
*/
public void cancel(Object tag) {
if (tag == null) return;
List<Pair<Integer, Disposable>> disposableList;
disposableList = mDisposableCache.get(tag.hashCode());
if (disposableList != null) {
for (Pair<Integer, Disposable> pair : disposableList) {
pair.second.dispose();
}
mDisposableCache.remove(tag.hashCode());
}
}
/**
*
* @param hash
* @param key
* @return 返回是否成功删除
*/
public boolean dispose(int hash, @NonNull String key) {
List<Pair<Integer, Disposable>> list = mDisposableCache.get(hash);
Pair<Integer, Disposable> removePair = null;
if (list != null) {
for (Pair<Integer, Disposable> pair : list) {
if (key.hashCode() == pair.first) {
pair.second.dispose();
removePair = pair;
break;
}
}
}
if (list != null && removePair != null) {
list.remove(removePair);
return true;
}
return false;
}
8.绑定生命周期
public class HttpClient<T> implements GenericLifecycleObserver {
......
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (source.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
source.getLifecycle().removeObserver(this);
cancel(source);
}
}
......
}
至此,整个封装思路便讲完了。以下是源码路径: