OkHttp源码解读总结(九)--->okhttp的缓存策略

本文介绍了OkHttp的缓存策略,包括为何使用缓存以提高请求速度,如何开启和使用缓存,深入探讨了Cache.put()和Cache.get()方法的源码,总结了这两个方法的主要步骤,并指出其在CacheInterceptor中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OkHttp源码解读总结(九)—>okhttp的缓存策略

标签(空格分隔): OkHttp源码 学习笔记


前言
  • 以下的相关知识总结是通过慕课网的相关学习和自己的相关看法,如果有需要的可以去查看一下慕课网的相关教学,感觉还可以。

为什么要使用缓存

  • 一个优点就是让客户端下一次的网络请求节省更多的时间,更快的展示数据

如何开启和使用缓存功能的呢?

 new OkHttpClient.Builder()
                .connectTimeout(10000, TimeUnit.MILLISECONDS)
                .readTimeout(10000, TimeUnit.MILLISECONDS)
                .writeTimeout(10000, TimeUnit.MICROSECONDS)
                //直接这样使用  配置Cache   File对象  缓存大小
                .cache(new Cache(new File("cache"),24*1024*1024))
                .build();

Cache.put()方法源码

@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;
    }
    //非get方法不需要缓存
    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;
    }
    //当经过上述的逻辑之后 到这一步 证明是可以缓存的      //那么就通过传入的response响应   创建Entry对象  就是我们需要写入缓存的   把需要的一些属性  方法  地址  头部 信息  code 等
    Entry entry = new Entry(response);
    //可以看到这里采用的是DiskLruCache缓存策略
    //http://blog.csdn.net/guolin_blog/article/details/28863651  (Android DiskLruCache完全解析,硬盘缓存的最佳方案)
    DiskLruCache.Editor editor = null;
    try {
      //把这个请求url(经过转换MD5加密然后转十六进制)作为key
      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;
    }
  }

entry.writeTo()方法

public void writeTo(DiskLruCache.Editor editor) throws IOException {
      //使用的是okio
      BufferedSink sink = Okio.buffer(editor.newSink(ENTRY_METADATA));
      //缓存的请求地址
      sink.writeUtf8(url)
          .writeByte('\n');
      //缓存的请求方法
      sink.writeUtf8(requestMethod)
          .writeByte('\n');
      sink.writeDecimalLong(varyHeaders.size())
          .writeByte('\n');
          //对header头部进行遍历
      for (int i = 0, size = varyHeaders.size(); i < size; i++) {
      //名字
        sink.writeUtf8(varyHeaders.name(i))
            .writeUtf8(": ")
            //value
            .writeUtf8(varyHeaders.value(i))
            .writeByte('\n');
      }
      //缓存http的响应行StatusLine
      sink.writeUtf8(new StatusLine(protocol, code, message).toString())
          .writeByte('\n');
          //响应手部
      sink.writeDecimalLong(responseHeaders.size() + 2)
          .writeByte('\n');
          //头部
      for (int i = 0, size = responseHeaders.size(); i < size; i++) {
        sink.writeUtf8(responseHeaders.name(i))
            .writeUtf8(": ")
            .writeUtf8(responseHeaders.value(i))
            .writeByte('\n');
      }
      //发送时间
      sink.writeUtf8(SENT_MILLIS)
          .writeUtf8(": ")
          .writeDecimalLong(sentRequestMillis)
          .writeByte('\n');
          //响应时间
      sink.writeUtf8(RECEIVED_MILLIS)
          .writeUtf8(": ")
          .writeDecimalLong(receivedResponseMillis)
          .writeByte('\n');
     //判断是否是https请求
      if (isHttps()) {
      //相应的握手  等缓存
        sink.writeByte('\n');
        sink.writeUtf8(handshake.cipherSuite().javaName())
            .writeByte('\n');
        writeCertList(sink, handshake.peerCertificates());
        writeCertList(sink, handshake.localCertificates());
        sink.writeUtf8(handshake.tlsVersion().javaName()).writeByte('\n');
      }
      //关闭
      sink.close();
    }

new CacheRequestImpl(editor)

CacheRequestImpl实现了CacheRequest接口,主要暴漏给后面需要介绍的CacheInterceptor拦截器  缓存拦截器可以直接根据这个CacheRequest实现类 CacheRequestImpl直接写入和更新缓存数据

    CacheRequestImpl(final DiskLruCache.Editor editor) {
      this.editor = editor;
      //响应主体
      this.cacheOut = editor.newSink(ENTRY_BODY);
      //
      this.body = new ForwardingSink(cacheOut) {
        @Override public void close() throws IOException {
          synchronized (Cache.this) {
            if (done) {
              return;
            }
            done = true;
            writeSuccessCount++;
          }
          super.close();
          editor.commit();
        }
      };
    }
put()总结
  • 1、首先判断缓存请求方法是否是get方法
  • 2、如果符合缓存策略,那么创建一个Entry对象—>用于我们所要包装的缓存信息
  • 3、最终使用DiskLruCache进行缓存
  • 4、最后通过返回一个CacheRequestImpl对象,这个对象主要用于CacheInterceptor拦截器服务的。

Cache.get()方法源码

主要从缓存中读取缓存的response

@Nullable Response get(Request request) {
    //通过传入的Request对象获取请求地址  并通过key方法获取缓存的key
    String key = key(request.url());
    //缓存快照  记录缓存在特定时刻缓存的内容
    DiskLruCache.Snapshot snapshot;

    Entry entry;
    try {
      //通过key值获取value
      snapshot = cache.get(key);
      if (snapshot == null) {
        return null;
      }
    } catch (IOException e) {
      // Give up because the cache cannot be read.
      return null;
    }

    try {
      //通过获取到的这个snapshot获取到Source  最终创建entry对象
      entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    } catch (IOException e) {
      Util.closeQuietly(snapshot);
      return null;
    }
   //通过之前创建好的entry实例获取到我们缓存的response对象
    Response response = entry.response(snapshot);
    //响应和请求是否一一对应  如果不对应 直接返回null
    if (!entry.matches(request, response)) {
      //关闭流
      Util.closeQuietly(response.body());
      return null;
    }
    //最终返回我们缓存的response实例
    return response;
  }

entry.response(snapshot)方法


    public Response response(DiskLruCache.Snapshot snapshot) {
      String contentType = responseHeaders.get("Content-Type");
      String contentLength = responseHeaders.get("Content-Length");
      //创建request
      Request cacheRequest = new Request.Builder()
          .url(url)
          .method(requestMethod, null)
          .headers(varyHeaders)
          .build();
      return new Response.Builder()
          .request(cacheRequest)
          .protocol(protocol)
          .code(code)
          .message(message)
          .headers(responseHeaders)
          //响应体的读取
          .body(new CacheResponseBody(snapshot, contentType, contentLength))
          .handshake(handshake)
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(receivedResponseMillis)
          .build();
    }
get()方法总结
  • 1、根据请求url获取key(MD5解密)
  • 2、通过key值获取 DiskLruCache.Snapshot(缓存快照)
  • 3、通过获取到的这个snapshot获取到Source 最终创建entry对象
  • 4、通过entry和snapshot(缓存快照)获取缓存的response并返回

这里所总结的Cache的get和put的相关知识,主要是为了为CacheInterceptor这个缓存拦截器做铺垫。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值