Retrofit+RxJava+OKHttp接入指南
前面学习了Retrofit和OKHttp,就尝试在自己的项目中使用了一下。本文介绍了接入Retrofit的一些问题,可供想使用Retrofit的同学作为参考。
api请求添加公共的Headers和params
如果通过注解的方式,在每个请求接口上添加公共headers和params,代码会太繁琐和冗余。所以可通过拦截器添加共的headers和params。
我定义的拦截器CommonInterceptor的拦截方法如下:
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request oldRequest = chain.request();
// 添加公共的请求参数
HttpUrl.Builder authorizedUrlBuilder = oldRequest.url()
.newBuilder()
.scheme(oldRequest.url().scheme())
.host(oldRequest.url().host())
.addQueryParameter("from", "android")
.addQueryParameter("version", Config.SDK_VERSION);
// 添加公共的请求headers
Request newRequest = oldRequest.newBuilder()
.addHeader("User-Agent", "android_" + Config.SDK_VERSION +
";" + Config.UNICOM_USERAGENT)
.method(oldRequest.method(), oldRequest.body())
.url(authorizedUrlBuilder.build())
.build();
return chain.proceed(newRequest);
}
把拦截器通过OKHttpClent设置给Retrofit:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(commonInterceptor)
.build();
retrofit = new Retrofit.Builder()
.baseUrl(WebConfig.ONLINE_URL_PRE)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
查看请求和响应Log日志
使用Retrofit查看请求和响应日志也是通过拦截器实现,HttpLoggingInterceptor是OKHttp提供的Log拦截器可以直接使用:
@Override
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
//设置日志级别
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
把拦截器通过OKHttpClent设置给Retrofit,同上不赘述。
使用OKHttp数据缓存功能
OKHttp本身就支持数据缓存功能,只要在请求接口定义缓存标签:
/**
* 获取鉴权接口
*/
@Headers("Cache-Control:public ,max-age=2592000")
@GET("oauth/token")
Observable<Oauth.Result> getToken();
但有个问题,如果服务端不支持缓存,会返回一些干扰信息,会影响缓存功能,所以要使用拦截器清除干扰信息,拦截方法如下:
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (NetworkUtils.isNetworkConnected(PlayerManager.getAppContext())) {
String cacheControl = request.cacheControl().toString();
return response.newBuilder()
//清除头信息
.removeHeader("Pragma")
.header("Cache-Control", cacheControl)
.build();
}
return response;
}
把拦截器通过OKHttpClent设置给Retrofit:
CacheInterceptor cacheInterceptor = new CacheInterceptor();
//设置缓存目录、缓存大小
File httpCacheDirectory = new File(EnvironmentUtilities.getHttpCachePath());
int cacheSize = 10 * 1024 * 1024;
Cache cache = new Cache(httpCacheDirectory, cacheSize);
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(cacheInterceptor)
.cache(cache)
.build();
......
提示:该缓存是api级别的对数据整体缓存,整读整存。如果缓存数据跟业务管理密切需要部分更新数据,需要自己用数据库实现缓存功能。
统一处理网络请求状态、服务端返回状态码ErrorCode
每个接口在处理业务数据之前,都要处理网络请求状态、服务端返回状态码ErrorCode,这应该一个统一的地方处理。
构建DefaultObserver处理服务器响应数据。定义DefaultObserver类继承Observer,并重写相应的方法。
1、在onError()方法里
@Override
public void onError(Throwable e) {
LogUtils.e("Retrofit", e.getMessage());
mBaseImpl.dismissProgress();
// HTTP错误
if (e instanceof HttpException) {
onException(ExceptionReason.BAD_NETWORK);
// 连接错误
} else if (e instanceof ConnectException
|| e instanceof UnknownHostException) {
onException(CONNECT_ERROR);
// 连接超时
} else if (e instanceof InterruptedIOException) {
onException(CONNECT_TIMEOUT);
// 解析错误
} else if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) {
onException(PARSE_ERROR);
} else {
onException(UNKNOWN_ERROR);
}
}
onException方法中做各种异常处理,比如Toast提示。
2、在onNext统一处理服务器返回状态码
@Override
public void onNext(T response) {
mBaseImpl.dismissProgress();
if (!response.isError()) {
onSuccess(response);
} else {
onFail(response);
}
}
onFail方法处理,异常返回码的各种状态码,onSuccess为抽象方法,用户实现观察者时候实现该方法,真正的处理正常返回的业务数据。
Rxjava生命周期管理
异步处理,如果控制不好生命周期,会导致内存泄露和一些莫名其妙的问题。
封装BaseActivityRxjava生命周期管理。
public class BaseRxActivity extends AppCompatActivity implements BaseImpl {
private CustomProgressDialog mProgressDialog;
private CompositeDisposable disposables2Stop;// 管理Stop取消订阅者者
private CompositeDisposable disposables2Destroy;// 管理Destroy取消订阅者者
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (disposables2Destroy != null) {
throw new IllegalStateException("onCreate called multiple times");
}
disposables2Destroy = new CompositeDisposable();
}
public boolean addRxStop(Disposable disposable) {
if (disposables2Stop == null) {
throw new IllegalStateException(
"addUtilStop should be called between onStart and onStop");
}
disposables2Stop.add(disposable);
return true;
}
public boolean addRxDestroy(Disposable disposable) {
if (disposables2Destroy == null) {
throw new IllegalStateException(
"addUtilDestroy should be called between onCreate and onDestroy");
}
disposables2Destroy.add(disposable);
return true;
}
public void remove(Disposable disposable) {
if (disposables2Stop == null && disposables2Destroy == null) {
throw new IllegalStateException("remove should not be called after onDestroy");
}
if (disposables2Stop != null) {
disposables2Stop.remove(disposable);
}
if (disposables2Destroy != null) {
disposables2Destroy.remove(disposable);
}
}
public void onStart() {
super.onStart();
if (disposables2Stop != null) {
throw new IllegalStateException("onStart called multiple times");
}
disposables2Stop = new CompositeDisposable();
}
public void onStop() {
super.onStop();
if (disposables2Stop == null) {
throw new IllegalStateException("onStop called multiple times or onStart not called");
}
disposables2Stop.dispose();
disposables2Stop = null;
}
public void onDestroy() {
super.onDestroy();
if (disposables2Destroy == null) {
throw new IllegalStateException(
"onDestroy called multiple times or onCreate not called");
}
disposables2Destroy.dispose();
disposables2Destroy = null;
}
}
生命周期管理是通过任务管理器CompositeDisposable 来管理的,就是在执行请求时候将Disposable添加到一个CompositeDisposable ,在页面至后台或者销毁时候调用dispose,取消Rxjava生命周期。
Disposable添加是在Observer 的onSubscribe调用的
@Override
public void onSubscribe(Disposable d) {
// 在onStop中取消订阅
if (isAddInStop) {
mBaseImpl.addRxStop(d);
// 在onDestroy中取消订阅
} else {
mBaseImpl.addRxDestroy(d);
}
}
自定义Convert响应数据转换器
一般Retrofit会配套使用Gson使用,但是如果改造老项目,把json解析从手动解析换成Gson解析可能工作量巨大,还是个容易出错的体力话。特别的接口协议设计不规范,没有面相对象的思想的时候,你会改到想吐。所以这种办法是自定义Convert数据转换器,复用原来的json数据解析。
@Override
public T convert(ResponseBody responseBody) throws IOException {
T t;
try {
t = ReflectUtil.getTClassInstance(type);
t.parse(responseBody.string());
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
} catch (InstantiationException e) {
e.printStackTrace();
return null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
return t;
}
parse方法是我们项目自己手动解析的方法。
Retrofit直接请求完整的Url
我们老项目中,没有使用Param来组装请求,而是直接用字符串拼装完整的url,然后请求api。所以去改造成params的话,又是一个体力话,所以我们这种直接使用完整的url请求。幸好Retrofit提供了这样的标签。
@GET()
Observable<ResponseBody> getFile(@Url String url);
这种情况还适用于,url是个动态的地址,比如是服务端返回的地址。
OKHttp链接Https不校验身份
跟HttpcCient和HttpURLConnection一样需要重写SSLSocketFactory绕过ssl身份验证,重写HostNameVerifier并且不做主机名验证。
//自定义TrustManager,绕过ssl身份验证
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] x509Certificates,
String s) throws java.security.cert.CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}};
SSLContext sc = null;
try {
sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
OkHttpClient client = new OkHttpClient.Builder()
//重写HostNameVerifier,主机名验证直接返回true
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})
.sslSocketFactory(sc.getSocketFactory())
.build();
以上为我在接入Retrofit和OKHttp遇到的问题以及解决方案,大家还有其他问题可以留言,大家探讨后我再补充。
网友评论