Android 笔记: OkhttpInterceptor Ca

2020-03-06  本文已影响0人  silencefun

Anroid OKhttp笔记1 流程分析
Android OkhttpInterceptor 笔记:RetryAndFollowUpInterceptor
Android OkhttpInterceptor 笔记:BridgeInterceptor
Android OkhttpInterceptor 笔记:ConnectInterceptor
Android OkhttpInterceptor 笔记:CacheInterceptor
Android OkhttpInterceptor 笔记:CallServerInterceptor
Android Okhttp笔记:ConnectionPool
Android Okhttp3:Dispatcher分析笔记


一、流程代码逻辑分析

CacheInterceptor是okhttp中缓存拦截器,是负责http请求的缓存处理 ,流程:
1.读取缓存
2.创建缓存策略,强制缓存、对比缓存等,
3.根据策略,不使用网络,又没有缓存的直接报错,并返回错误码504。
4.根据策略,不使用网络,有缓存的直接返回。
5.前面两个都没有返回,继续执行下一个Interceptor,即ConnectInterceptor。
6.接收到网络结果,如果响应code式304,则使用缓存,返回缓存结果。
7.读取网络结果。
8.对数据进行缓存。
9.返回网络读取的结果。

其中header 强制缓存使用的的两个标识:
Expires:Expires的值为服务端返回的到期时间,即下一次请求时,请求时间小于服务端返回的到期时间,直接使用缓存数据。到期时间是服务端生成的,客户端和服务端的时间可能有误差。
Cache-Control:Expires有个时间校验的问题,所以HTTP1.1采用Cache-Control替代Expires。

Cache-Control的取值:

private::客户端可以缓存。
public::客户端和代理服务器都可缓存。
max-age=xxx: 缓存的内容将在 xxx 秒后失效
no-cache::需要使用对比缓存来验证缓存数据。
no-store:所有内容都不会缓存;强制缓存,对比缓存都不会触发。

1.流程分析

当从上个拦截器中获取到http请求时,会从缓存里面取出对应的响应(之前缓存过的),如果没有,返回null。然后会根据request和获取到的缓存的response生成一个缓存策略CacheStrategy。

//如果配置了缓存:优先从缓存中读取Response
Response cacheCandidate = cache != null
    ? cache.get(chain.request())
    : null;
long now = System.currentTimeMillis();
//缓存策略,该策略通过某种规则来判断缓存是否有效
 CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
Request networkRequest = strategy.networkRequest;
Response cacheResponse = strategy.cacheResponse;

缓存策略器是使用缓存还是请求网络获取新的数据。内部有两个属性:networkRequest和cacheResponse,在 CacheStrategy 内部会对这个两个属性在特定的情况赋值。

networkRequest:若是不为 null ,表示需要进行网络请求
cacheResponse:若是不为 null ,表示可以使用本地缓存

//如果根据缓存策略strategy禁止使用网络,并且缓存无效,直接返回空的Response
if (networkRequest == null && cacheResponse == null) {
  return new Response.Builder()
      。。。
      .code(504)
      .message("Unsatisfiable Request (only-if-cached)")
      .body(Util.EMPTY_RESPONSE)//空的body
      。。。
      .build();
}

//如果根据缓存策略strategy禁止使用网络,且有缓存则直接使用缓存
if (networkRequest == null) {
  return cacheResponse.newBuilder()
      .cacheResponse(stripBody(cacheResponse))
      .build();
}

//需要网络
Response networkResponse = null;
try {//执行下一个拦截器,发起网路请求
  networkResponse = chain.proceed(networkRequest);
} finally {
  。。。
}

//本地有缓存,
if (cacheResponse != null) {
  //并且服务器返回304状态码(说明缓存还没过期或服务器资源没修改)
  if (networkResponse.code() == HTTP_NOT_MODIFIED) {
    //使用缓存数据
    Response response = cacheResponse.newBuilder()
        。。。
        .build();
      。。。。
     //返回缓存 
    return response;
  } else {
    closeQuietly(cacheResponse.body());
  }
}

//如果网络资源已经修改:使用网络响应返回的最新数据
Response response = networkResponse.newBuilder()
    .cacheResponse(stripBody(cacheResponse))
    .networkResponse(stripBody(networkResponse))
    .build();

//将最新的数据缓存起来
if (cache != null) {
  if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {

    CacheRequest cacheRequest = cache.put(response);
    return cacheWritingResponse(cacheRequest, response);
  }
//返回最新的数据
return response;

2.CacheStrategy

策略器,负责判断是使用缓存还是请求网络获取新的数据。

获取方法

CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();

其中 cacheCandidate它表示的是从缓存中取出的 Response 对象,有可能为null(在缓存为空的时候),在 new CacheStrategy.Factory 内部如果 cacheCandidate 对象不为 null ,那么会取出 cacheCandidate 的头信息,并且将其保存到 CacheStrategy 属性中。

get()

public CacheStrategy get() {
  CacheStrategy candidate = getCandidate();

  if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
    // We're forbidden from using the network and the cache is insufficient.
    return new CacheStrategy(null, null);
  }
  return candidate;
}
private CacheStrategy getCandidate() {
  // No cached response.
  if (cacheResponse == null) {
    return new CacheStrategy(request, null);
  }
  // Drop the cached response if it's missing a required handshake.
  if (request.isHttps() && cacheResponse.handshake() == null) {
    return new CacheStrategy(request, null);
  }
  // If this response shouldn't have been stored, it should never be used
  // as a response source. This check should be redundant as long as the
  // persistence store is well-behaved and the rules are constant.
  if (!isCacheable(cacheResponse, request)) {
    return new CacheStrategy(request, null);
  }
  CacheControl requestCaching = request.cacheControl();
 //....
}

代码显示的几种情况都是要发起请求的,包括:
没有对应的缓存结果;
https请求却没有握手信息;
不允许缓存的请求(包括一些特殊状态码以及Header中明确禁止缓存)。

3.实现缓存

 // 存入到缓存中去
    CacheRequest cacheRequest = cache.put(response); 


  @Nullable CacheRequest put(Response response) {
String requestMethod = response.request().method();
if (HttpMethod.invalidatesCache(response.request().method())) {
  try {
    remove(response.request());
  } catch (IOException ignored) {
    // The cache cannot be written.
  }
  return null;
}
if (!requestMethod.equals("GET")) {
  // Don't cache non-GET responses. We're technically allowed to cache
  // HEAD requests and some POST requests, but the complexity of doing
  // so is high and the benefit is low.
  return null;
}
if (HttpHeaders.hasVaryAll(response)) {
  return null;
}
Entry entry = new Entry(response);
DiskLruCache.Editor editor = null;
try {
  editor = cache.edit(key(response.request().url()));
  if (editor == null) {
    return null;
  }
  entry.writeTo(editor);
  return new CacheRequestImpl(editor);
} catch (IOException e) {
  abortQuietly(editor);
  return null;
}
 }

其中 invalidatesCache()

   public static boolean invalidatesCache(String method) {
return method.equals("POST")
    || method.equals("PATCH")
    || method.equals("PUT")
    || method.equals("DELETE")
    || method.equals("MOVE");     // WebDAV

}
对方法是POST,PATCH,PUT,DELETE,MOVE的请求,将缓存清除掉,这些是不应该被缓存的。然后再明确确认GET方法才会被缓存。
然后由 Cache.Entry 构造的entry writeTo把editor 写入,其实就是将请求信息按顺序写入到DiskLruCache中,最终由DiskLruCache写入到磁盘中。


Anroid OKhttp笔记1 流程分析
Android OkhttpInterceptor 笔记:RetryAndFollowUpInterceptor
Android OkhttpInterceptor 笔记:BridgeInterceptor
Android OkhttpInterceptor 笔记:ConnectInterceptor
Android OkhttpInterceptor 笔记:CacheInterceptor
Android OkhttpInterceptor 笔记:CallServerInterceptor
Android Okhttp笔记:ConnectionPool
Android Okhttp3:Dispatcher分析笔记

上一篇下一篇

猜你喜欢

热点阅读