Okhttp的缓存策略有如下几种:
boolean noCache;//等于FORCE_NETWORK,不使用缓存
boolean noStore;//不缓存,不存储
int maxAgeSeconds = -1;//缓存的有效时间
int maxStaleSeconds = -1;//缓存的延长有效时间
int minFreshSeconds = -1;//增加额外的缓存的有效时间,在stale前计算
boolean onlyIfCached;//等于FORCE_CACHE,有缓存就用缓存
boolean noTransform;//不接受经过转码的响应
boolean immutable;//缓存有效时间内,响应不会变化,避免服务器处理304响应
如果这些缓存策略可以满足需求,那就不用再往下看了,相关的文章有很多
如果需要有网络用网络,无网络(网络请求失败)时使用缓存,或其他自定义策略,可以参考下面的方法
首先需要明确几个要求
1.使用拦截器来实现,改动小,作用全局
2.方便控制每个接口是否缓存
3.接口参数有改动时也能命中缓存
实现
一.存取
理想的情况下最好是将整个Response都存储起来,但是看一眼源代码
public final class Response implements Closeable
public interface Closeable extends AutoCloseable
public interface AutoCloseable
整个继承和实现的链条里跟序列化没有一毛钱关系,并且是不能被继承的final class
那既然不能保存Response,是否可以自己组装一个,答案是肯定的
Response通过Builder方式创建,这个Builder也是public的可以调用到
public Builder() {
headers = new Headers.Builder();
}
那组装时需要哪些参数呢,点一下试试
emmm...有点多,以后再详细说明这些参数,先偷个懒,可以先看一下哪里用到了这个Builder
这里有okhttp自己实现的缓存拦截器,进去看看
return new Response.Builder()
.request(chain.request())//原始请求,这个在拦截器中可以直接拿到
.protocol(Protocol.HTTP_1_1)//协议,直接填就行
.code(504)//code码,可以直接填200
.message("Unsatisfiable Request (only-if-cached)")//可以随便写,我这里写得“use cache”便于以后统计
.body(Util.EMPTY_RESPONSE)//数据内容*
.sentRequestAtMillis(-1L)//请求时间
.receivedResponseAtMillis(System.currentTimeMillis())//返回时间
.build();
这里有一个现成的例子
上面代码中的*位置就是数据内容保存的地方,这里需要添加一个ResponseBody类型的参数,好在该类中有方便创建对象的方法
public static ResponseBody create(@Nullable MediaType contentType, String content)
这个方法有两个参数,第一个是内容类型,这个类中也有方便的方法
public static @Nullable MediaType parse(String string)//在一般返回的请求中直接使用MediaType.parse("application/json; charset=UTF-8")即可
第二个参数就是内容主体了,就是服务端返回的数据内容
构造Response现在已经清楚了,那现在就可以在保存缓存时只进行数据内容的保存即可,那应该如何保存呢
这里可以使用下面的代码获取到body中的数据并转为string类型
response.peekBody(Long.MAX_VALUE).string()
然后拿到这个数据可以用自己喜欢的方式进行持久化保存
简单使用可以用HashTable进行<Url,data>键值对保存,然后将其这个map整个写入到一个文件中,在使用时再整个读取出来,方便使用(或者使用xml,json等格式化数据),ps:为了安全起见只应该保存Get方法的数据,最好使用数据库进行持久化存储
这里需要注意如果使用HashMap可能会出现java.util.ConcurrentModificationException异常
二.根据接口选择是否缓存
这里的方法就很多了,说一个比较简单的
可以在请求时添加一个临时header,然后再拦截器中读取这个header进行辨认并将这个header删除(以免对请求产生不必要的影响)
如果使用retrofit进行网络请求,这种方式在Retrofit中使用多个baseUrl有详细说明
三.网络请求失败时命中缓存
这里只需要对拦截器中请求得部分进行try-catch即可,在网络请求失败时okhttp会抛错,这时再catch块中进行缓存的返回即可
需要注意的是下面的集中情况需要过滤,这是主动打断请求时会抛出的错误
e.toString().contains("Canceled")
e.toString().contains("InterruptedIOException")
e.toString().contains("CANCEL")
四.在请求参数改变时仍然命中(忽略某些参数)
这里需要对请求参数的改变有把握,比如net_type在使用移动网络的情况下应该可以命中wifi时缓存的数据
其实没啥可说的,将请求中的参数通过字符串分割的方式去掉其中的某些参数即可,需要注意的是保存和获取前的对比都需要去掉这些参数