前一篇文章(也是我在简书上的第一篇技术文章^.^)讲了Android三剑客的基础用法和简单封装,有一些封装只是一笔带过,还有些用法被遗漏没讲到的,所以在这篇里统一做下查漏补缺。
0x00 先做一下纠正:
https和失败重连,OkHttp默认是支持的,并不用手动去设置(在OkHttpClient.Builder中已默认设置),所以OkHttpClient.Builder的初始化可以简化为:
|
0x01 Cookie持久化管理
这部分主要参考了这篇文章。
- 不带持久化builder.cookieJar(new CookieJar() {private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {cookieStore.put(url, cookies);}public List<Cookie> loadForRequest(HttpUrl url) {List<Cookie> cookies = cookieStore.get(url);return cookies != null ? cookies : new ArrayList<Cookie>();}});
这种简单的实现,每次重启App都会需要重新登录,不可取。
- 带持久化CookieHandler cookieHandler = new CookieManager(new PersistentCookieStore(context), CookiePolicy.ACCEPT_ALL);builder.cookieJar(new JavaNetCookieJar(cookieHandler));
这里出现了两个类:JavaNetCookieJar和PersistentCookieStore
JavaNetCookieJar就是对CookieJar的封装实现,里面实现了对Cookie持久化存储和获取的调用逻辑,OkHttp已经帮我们实现了这个类,需要引入下面这个包:
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.5.0'PersistentCookieStore是具体实现Cookie持久化的类,使用的是SharedPreferences,具体代码实现可参考这篇。
当然,如果你想通过数据库实现持久化,也可以自己封装一个类似的类去实现。
- 再介绍一个封装了Cookie持久化的第三方库(推荐)ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(context));builder.cookieJar(cookieJar);
需要引入下面这个包:
|
0x02. 拦截器
addInterceptor和addNetworkInterceptor的区别
前一篇文章有同学问到两者的区别,okHttp官方对拦截器做了解释,并给了一张图,感觉挺一目了然的。
两种拦截器简单来说就是调用时机的区别,应用拦截器调用时机较早,也就是进入chain.proceed的递归较早,相应的完成递归得到response会较晚;而网络拦截器则相反,request请求调用时机较晚,会较早完成chain.proceed递归调用,得到response的时机较早。
简单来说就是应用拦截器较上层,而网络拦截器较底层,所有拦截器就是一个由浅入深的递归调用。具体还是得看源码。Http Header
可以通过这个拦截器为Request添加全局统一的Header。/*** 网络请求公共头信息插入器** Created by XiaoFeng on 17/1/18.*/public class HttpHeaderInterceptor implements Interceptor {public Response intercept(Chain chain) throws IOException {Request original = chain.request();Request request = original.newBuilder().header("User-Agent", "Android, xxx").header("Accept", "application/json").header("Content-type", "application/json").method(original.method(), original.body()).build();return chain.proceed(request);}}
公共参数
主要参考这篇/*** 网络请求公共参数插入器* <p>* Created by XiaoFeng on 2017/1/25.*/public class CommonParamsInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();if (request.method().equals("GET")) {HttpUrl httpUrl = request.url().newBuilder().addQueryParameter("version", "xxx").addQueryParameter("device", "Android").addQueryParameter("timestamp", String.valueOf(System.currentTimeMillis())).build();request = request.newBuilder().url(httpUrl).build();} else if (request.method().equals("POST")) {if (request.body() instanceof FormBody) {FormBody.Builder bodyBuilder = new FormBody.Builder();FormBody formBody = (FormBody) request.body();for (int i = 0; i < formBody.size(); i++) {bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i));}formBody = bodyBuilder.addEncoded("version", "xxx").addEncoded("device", "Android").addEncoded("timestamp", String.valueOf(System.currentTimeMillis())).build();request = request.newBuilder().post(formBody).build();}}return chain.proceed(request);}}缓存策略
/*** 网络请求缓存策略插入器** Created by XiaoFeng on 17/1/17.*/public class HttpCacheInterceptor implements Interceptor {@Overridepublic Response intercept(Chain chain) throws IOException {Request request = chain.request();// 无网络时,始终使用本地Cacheif (!NetworkUtil.isNetworkConnected()) {request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();}Response response = chain.proceed(request);if (NetworkUtil.isNetworkConnected()) {// 有网络时,设置缓存过期时间0个小时int maxAge = 0;response.newBuilder().header("Cache-Control", "public, max-age=" + maxAge).removeHeader("Pragma") // 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效.build();} else {// 无网络时,设置缓存过期超时时间为4周int maxStale = 60 * 60 * 24 * 28;response.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale).removeHeader("Pragma").build();}return response;}}调试工具
使用的是Facebook推出的一个集成到Chrome中的调试工具,需要引入下面两个库:compile 'com.facebook.stetho:stetho:1.4.1'compile 'com.facebook.stetho:stetho-okhttp3:1.4.1'
在Application中初始化就可以用了
|
如何调试?
- 打开Chrome浏览器
- 地址栏输入
chrome://inspect
- 进入页面后,在左边的DevTools -> Devices -> Remote Target下,可以找到你连接的手机设备,点开后就会出现调试页面了,后面就自己研究吧,不光可以调试网络请求,还可以查看手机中的数据库和SharePreference等持久化文件,而且不用root,很强大!
0x03. FastJson解析库封装
网上很多介绍retrofit的文章,对网络请求返回的结果,使用的都是默认的Gson库,虽然可以满足大部分人的需求,但是有些对性能要求高一点的人,还是习惯使用FastJson库做解析,这里就讲讲如何把默认的Gson库替换成FastJson库。
首先,默认Gson库的设置是这样的:
|
用FastJson库替换后是这样的:
|
是不是很像,没错,就是把ConverterFactory替换了一下而已。
至于FastJsonConvertFactory的实现,其实就是仿造GsonConverterFactory的源码来写的,并不复杂。
主要有三个类:
工厂类:FastJsonConvertFactory,里面就是分别创建了Request和Response的转换类。
/**** Created by XiaoFeng on 2017/1/17.*/public class FastJsonConvertFactory extends Converter.Factory {public static FastJsonConvertFactory create() {return new FastJsonConvertFactory();}public Converter<?, RequestBody> requestBodyConverter(Type type, Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {return new FastJsonRequestConverter<>();}public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {return new FastJsonResponseConverter<>(type);}}Request转换类:FastJsonRequestConverter
/**** Created by XiaoFeng on 2017/1/17.*/public class FastJsonRequestConverter<T> implements Converter<T, RequestBody> {private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");private static final Charset UTF_8 = Charset.forName("UTF-8");public RequestBody convert(T value) throws IOException {return RequestBody.create(MEDIA_TYPE, JSON.toJSONBytes(value));}}Response转换类:FastJsonResponseConverter
/**** Created by XiaoFeng on 2017/1/17.*/public class FastJsonResponseConverter<T> implements Converter<ResponseBody, T> {private final Type type;public FastJsonResponseConverter(Type type) {this.type = type;}public T convert(ResponseBody value) throws IOException {BufferedSource buffer = Okio.buffer(value.source());String s = buffer.readUtf8();buffer.close();return JSON.parseObject(s, type);}}
是不是很简单,如果想再换成别的第三方json解析库,照着这个写就可以了。
0x04. 生命周期
上一篇中还有同学提到RxJava的生命周期管理,防止内存泄漏,这个可以直接使用第三方库,参考这篇。
一般引用下面两个库就够了:
|
有两种使用方式:
1. 自动取消订阅,使用bindToLifecycle。
需要继承至RxActivity或者RxFragment等基类。
|
从下面这段核心函数可以看清自动取消订阅的规则,就是在哪个生命周期内调用bindToLifecycle,就在与其对应的结束生命周期函数调用时自动取消订阅。
|
2. 手动取消订阅,使用bindUntilEvent。
需要继承至RxActivity或者RxFragment等基类。
|
3. 自定义RxActivity/RxFragment
直接继承RxActivity/RxFragment有时会碰到问题,因为有可能本身已经有一个基类需要继承,java不能多继承。不过不要慌,我们可以自定义一个自己的基类,实现方式参考RxActivity。
|
突然发现写文章真是一个知识梳理,自我学习的好方法,比没有目的性的看很多技术文章有用很多倍,极力推荐有能力的同学都去尝试写属于自己的技术博客。^ ^
参考:
https://gold.xitu.io/entry/572ed42ddf0eea0063186e1f
https://gist.github.com/franmontiel/ed12a2295566b7076161
https://gold.xitu.io/entry/5825300b2f301e005c47fac5
http://www.codexiu.cn/android/blog/39432/
http://androidxx.ren/forum.php?mod=viewthread&tid=19
https://gold.xitu.io/entry/58290ea2570c35005878ce8f