OKHttp源码学习——缓存篇

OKHttp真实调用请求的类是RealCall

Dispatcher该类是作为请求分发

  //异步请求最多的请求个数
  private int maxRequests = 64;
  //同一个host最多异步请求的个数
  private int maxRequestsPerHost = 5;
  //线程池
  private ExecutorService executorService;
  //即将要请求的
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
  //正在请求的
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
  //同步请求
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

异步请求时如果最多异步请求超过64,并且同一主机请求个数超过了5就,放入到readyAsyncCalls

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

当网络请求完毕之后会调用finish方法,从readyAsyncCalls中把准备请求的拿出来

synchronized void finished(AsyncCall call) {
	//将同步请求移除
    if (!runningAsyncCalls.remove(call)) throw new AssertionError("AsyncCall wasn't running!");
    //将readyAsyncCalls中等待的realCall放入线程池中
    promoteCalls();
  }

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();

      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }

      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

同步请求时直接将RealCall放入到runningSyncCalls

synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

拦截器

@Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain(forWebSocket);
        if (canceled) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          logger.log(Level.INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

ApplicationInterceptorChain这是一个拦截器,用户可以自己定义拦截器进行拦截
在这里插入图片描述

如果没有拦截器,那么就可以进行网络请求了,在getResponse方法中实例化了HttpEngine类

发送请求sendRequest

networkRequest:只是设置了一些请求头的操作

private Request networkRequest(Request request) throws IOException {
    Request.Builder result = request.newBuilder();

    if (request.header("Host") == null) {
      result.header("Host", hostHeader(request.url(), false));
    }

    if (request.header("Connection") == null) {
      result.header("Connection", "Keep-Alive");
    }

    if (request.header("Accept-Encoding") == null) {
      transparentGzip = true;
      result.header("Accept-Encoding", "gzip");
    }

    List<Cookie> cookies = client.cookieJar().loadForRequest(request.url());
    if (!cookies.isEmpty()) {
      result.header("Cookie", cookieHeader(cookies));
    }

    if (request.header("User-Agent") == null) {
      result.header("User-Agent", Version.userAgent());
    }

    return result.build();
  }

下面是缓存的策略,重点啊~
如果你的代码设置

File cacheFile = new File(context.getExternalCacheDir().toString(),"cache");
        //缓存大小为10M
        int cacheSize = 10 * 1024 * 1024;
        //创建缓存对象
        final Cache cache = new Cache(cacheFile,cacheSize);
        
		OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(mConnectTimeout, TimeUnit.SECONDS)
                .readTimeout(mReadTimeOut, TimeUnit.SECONDS)
                .writeTimeout(mWriteTimeOut, TimeUnit.SECONDS)
                .cache(cache);

那么将会在访问网络之后

if (hasBody(userResponse)) {
      maybeCache();
      userResponse = unzip(cacheWritingResponse(storeRequest, userResponse));
    }

将响应体写到Cache里面,采用LRU算法,Key是一个32位长度的MD5值,Value是文件封装的对象
当put存入之后,你下次responseCache.get就是上一次的响应

InternalCache responseCache = Internal.instance.internalCache(client);
    Response cacheCandidate = responseCache != null
        ? responseCache.get(request)
        : null;

但是光拿到上一次的响应头还不算完,还得进行一些判断,目的是判断是否需要重新进行网络请求,因为可能响应头的资源已经过期,超出了规定的时间,就不能在用了,要重新进行网络请求

public Factory(long nowMillis, Request request, Response cacheResponse) {
	  //这个是当前系统的时间
      this.nowMillis = nowMillis;
      //请求
      this.request = request;
      //缓存中的响应
      this.cacheResponse = cacheResponse;
		
      if (cacheResponse != null) {
        Headers headers = cacheResponse.headers();
        for (int i = 0, size = headers.size(); i < size; i++) {
          String fieldName = headers.name(i);
          String value = headers.value(i);
          if ("Date".equalsIgnoreCase(fieldName)) {
            servedDate = HttpDate.parse(value);
            servedDateString = value;
          } else if ("Expires".equalsIgnoreCase(fieldName)) {
            expires = HttpDate.parse(value);
          } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
            //服务器资源修改的时间
            lastModified = HttpDate.parse(value);
            lastModifiedString = value;
          } else if ("ETag".equalsIgnoreCase(fieldName)) {
            etag = value;
          } else if ("Age".equalsIgnoreCase(fieldName)) {
            //首部字段Age能告知客户端,源服务器在多久前创建了响应,字段值的单位为秒。
            //若创建该响应的服务器是缓存服务器,Age值是指缓存后的响应再次发起认证到认证
            //完成的时间值
            ageSeconds = HeaderParser.parseSeconds(value, -1);
          } else if (OkHeaders.SENT_MILLIS.equalsIgnoreCase(fieldName)) {
            //发送请求的本地时间。
            sentRequestMillis = Long.parseLong(value);
          } else if (OkHeaders.RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
            //收到响应的本地时间。
            receivedResponseMillis = Long.parseLong(value);
          }
        }
      }
    }

只是对响应头进行了一些抽取,接下来

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;
    }

重点先看下get方法里面的getCandidate方法,缓存策略的重点

private CacheStrategy getCandidate() {
      // No cached response.
      if (cacheResponse == null) {
      	//如果响应头是null,那么就得请求网络了
        return new CacheStrategy(request, null);
      }
	
      //如果缺少必要的握手,则删除缓存的响应,也得去请求网络
      if (request.isHttps() && cacheResponse.handshake() == null) {
        return new CacheStrategy(request, null);
      }

	  //主要是对各种状态进行检查
	  //这里面注意下这样代码
	  //!response.cacheControl().noStore() && !request.cacheControl().noStore();
	  //no-store这个响应字段含义:简单来说就是不能有缓存,应用于机密文件
      if (!isCacheable(cacheResponse, request)) {
        return new CacheStrategy(request, null);
      }
	  //请求对象获取cacheControl对象,Request可以通过cacheControl方法进行设置
      CacheControl requestCaching = request.cacheControl();
      //requestCaching.noCache()是否需要缓存
      //hasConditions(request)
      //request.header("If-Modified-Since") != null || request.header("If-None-Match") != null
      //If-Modified-Since和If-None-Match你可以理解成数据库的条件语句
      //你想啊,有条件,证明每次请求都会不一样,那么当然需要重新请求网络了
      //实在好奇就自己百度下吧
      if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }
      //该方法里面有这样一句receivedAge + responseDuration + residentDuration
      //就是现在的我距离上次发送请求的时间
      long ageMillis = cacheResponseAge();
      long freshMillis = computeFreshnessLifetime();
	
      if (requestCaching.maxAgeSeconds() != -1) {
        freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
      }

      long minFreshMillis = 0;
      if (requestCaching.minFreshSeconds() != -1) {
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
      }

      long maxStaleMillis = 0;
      //我在自己的代码中加入了拦截器
      /* class CacheInterceptor implements Interceptor{

        @Override
        public Response intercept(Chain chain) throws IOException {

            Response originResponse = chain.proceed(chain.request());

            //设置缓存时间为60秒,并移除了pragma消息头,移除它的原因是因为pragma也是控制缓存的一个消息头属性
            return originResponse.newBuilder().removeHeader("pragma")
                    .header("Cache-Control","max-age=60").build();
        }
    }
*/
 	  //这句话就是得到我设置缓存过期时间
      CacheControl responseCaching = cacheResponse.cacheControl();
      if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
      }
      //如果我第二次发送响应的时间小于我设置缓存的时间,那么恭喜你,你就可以走缓存,不用
      //重新访问网络了
      if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        Response.Builder builder = cacheResponse.newBuilder();
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
        }
        long oneDayMillis = 24 * 60 * 60 * 1000L;
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
        }
        return new CacheStrategy(null, builder.build());
      }

      Request.Builder conditionalRequestBuilder = request.newBuilder();
      //为请求增加条件
      if (etag != null) {
        conditionalRequestBuilder.header("If-None-Match", etag);
      } else if (lastModified != null) {
        conditionalRequestBuilder.header("If-Modified-Since", lastModifiedString);
      } else if (servedDate != null) {
        conditionalRequestBuilder.header("If-Modified-Since", servedDateString);
      }
      //如果已经有条件了,那么就不能走缓存了,需要重新进行网络请求
      Request conditionalRequest = conditionalRequestBuilder.build();
      return hasConditions(conditionalRequest)
          ? new CacheStrategy(conditionalRequest, cacheResponse)
          : new CacheStrategy(conditionalRequest, null);
    }

以上就是缓存的策略

//假如你现在没有缓存,并且你还设置了CacheControl.FORCE_CACHE这个参数,那么你就会收
//到504的异常
if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
        // We're forbidden from using the network and the cache is insufficient.
        return new CacheStrategy(null, null);
      }

回到上面接着说

if (responseCache != null) {
	  //记录当前请求是网络发起还是缓存发起
      responseCache.trackResponse(cacheStrategy);
    }
    
    if (cacheCandidate != null && cacheResponse == null) {
      closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
    }

    //504错误
    if (networkRequest == null && cacheResponse == null) {
      userResponse = new Response.Builder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .protocol(Protocol.HTTP_1_1)
          .code(504)
          .message("Unsatisfiable Request (only-if-cached)")
          .body(EMPTY_BODY)
          .build();
      return;
    }

    //如果networkRequest为null那么就可以走缓存了,这种情况在前面讲过
    if (networkRequest == null) {
      userResponse = cacheResponse.newBuilder()
          .request(userRequest)
          .priorResponse(stripBody(priorResponse))
          .cacheResponse(stripBody(cacheResponse))
          .build();
      userResponse = unzip(userResponse);
      return;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值