HTTTP 缓存机制

一、概述

最近想尝试写一个关于网络请求的系列文章,将网络请求的基础、使用及网络框架的学习分析总结以下,大致准备从以下几个方面分析:

  1. 网络请求的基础
  2. HTTP请求方式和报文解析
  3. Cookie 和 Session的理解与使用
  4. HTTP Cache缓存机制
  5. 封装网络请求
  6. TCP 和 Socket

HTTP的缓存机制,对于Web端的开发人员可能会比较熟悉,可作为移动端的开发使用的是限量的流量,所以对缓存和减少流量的要求更为需要,不过好在平时我们使用的网络框架都封装了如何设置缓存的方法,但从学习的角度还是要知道Http的协议是如何缓存文件和如何更新信息的。

二、缓存介绍

相信从缓存的名字上可以理解,及时保存已获取的数据下次直接使用,减少对服务器的请求和网络的消耗,看看缓存的执行流程:

HTTTP 缓存机制_第1张图片

  1. 程序第一次请求数据首先去缓存文件中,根据请求的url查询是否存在返回信息,如果存在则直接返回
  2. 如果缓存中没有数据,则直接去服务器读取数据,并携带服务器设定的缓存机制
  3. 获取数据显示后,根据设定的缓存机制保存请求的数据

 

三、缓存的分类可控制

  • 强制缓存:当本地有缓存文件切未失效时直接加载缓存,否则去网络获取并缓存

HTTTP 缓存机制_第2张图片

  • 对比缓存:首先读取本地缓存文件的标志,将此标志发送到服务端,服务端验证缓存数据是否失效,如果未失效返回304,则客户端会直接读取缓存文件,如果失效则返回新的数据和缓存配置

HTTTP 缓存机制_第3张图片

      对比缓存又可以分为:Last-Modified  /  If-Modified-Since 和 Etag  /  If-None-Match 成对配合设置缓存;下面会详细介绍客户端和服务器如何实现缓存机制的。

  • Cache-control:Http的缓存主要利用Header里的Cache-control字段来控制,包含以下字段
  1. private:只有客户端缓存
  2. public:客户端和服务器端都可以缓存
  3. max-age:缓存的过期时间,当参过这个时间缓存就会失效
  4. no-Cache:需要数据对比验证缓存数据是否有效 
  5. no-store:不进行缓存
  • 强制缓存实现

HTTTP 缓存机制_第4张图片

  1. 强制缓存的实现比较简单,是靠服务端返回的max-age设置的有效期控制,只要在设定的有效期之内数据均有效,且直接加载数据
  • 比较缓存实现
  1. Last-Modified  /  If-Modified-Since:根据修改的时间确定数据是否刷新

HTTTP 缓存机制_第5张图片

 

(1) Last-Modified :首次请求时服务端返回的Head中包含此字段,值为最新修改如期,客户端收到后储存信息
(2) If-Modified-Since:之后Request的Header中包含字段,并传递上次保存的日期
(3) 服务端收到请求后比较上传的修改日期与服务端的修改日期,确定数据是否更新

     2、Etag  /  If-None-Match:服务器端的资源标志,用来对比缓存

HTTTP 缓存机制_第6张图片


(1)Etag  :客户端第一次请求时会返回资源的Etag,客户端收到后储存数据
(2)If-None-Match:下次请求时会在Header的If-None-Match中携带Etag
(3)服务端收到Etag后进行对比,确定时返回304还是新资源

四、okHttp中缓存的使用

  • 创键缓存文件夹及Cache
val file = File(activity!!.externalCacheDir.absolutePath + File
        .separator + "cache")
val cache = Cache(file, 1021 * 1024 * 10)
  • HTTP配置缓存
val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(CacheInterceptor())
        .cache(cache)
        .build()

val request = Request.Builder()
        .url(url)
        .build()

var response = okHttpClient.newCall(request).execute()
System.out.println("Body Response = " + response.body().toString())
System.out.println("Cache Response = " + response.cacheResponse())
System.out.println("NetWork Response = " + response.networkResponse())
response.body()?.close()
var responseCache = okHttpClient.newCall(request).execute()
System.out.println("Body Response = " + responseCache.body().toString())
System.out.println("NetWork Response = " + responseCache.networkResponse())
System.out.println("Cache Response = " + responseCache.cacheResponse())
responseCache.body()?.close()
  • 两次请求后的数据输出为

  • 查看服务器的缓存时间为:3600 s(有效时间)

HTTTP 缓存机制_第7张图片

  • 再次获取时发现确实只从缓存读取

  • 那如果此时我们就是想从网络获取呢?使用 Cache-Control 策略
val request = Request.Builder()
        .cacheControl(CacheControl.FORCE_NETWORK)  // 强制使用网络请求
        .url(url)
        .build()
  • 此时输出的:

 

  • 查看缓存文件
  1. 手机文件夹 Android/data/com.***/cache/

HTTTP 缓存机制_第8张图片

   2、查看缓存的文件信息

HTTTP 缓存机制_第9张图片HTTTP 缓存机制_第10张图片

五、缓存拦截器 (Interceptor)

在拦截器中会获取请的的Request 和 返回的 Response ,我们可以在发起请求或接收结果时修改Header,实现想要的功能

  • 创建 Interceptor的实现类,并重写intercept方法(实现有网和无网不同的缓存)
public class CacheInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();  // 获取请求的Request
        if (NetworkConnectionUtils.INSTANCE.isNetworkConnected(AppUtils.INSTANCE.getContext())) {
            // 有网络时, 缓存10秒钟
            int maxAge = 10;
            request = request.newBuilder()  // 修改Request 的Header
                    .removeHeader("User-Agent")
                    .header("User-Agent", HttpUtils.INSTANCE.getUserAgent())
                    .build();

            Response response = chain.proceed(request);  // 获取返回的Response
            return response.newBuilder()    // 修改Response 的Header
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, max-age=" + maxAge) 
                     // 设置Cache-Control 
                    .build();
        } else {
            // 无网络时,缓存为4周
            int maxStale = 60 * 60 * 24 * 28;
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)// 强制使用缓存
                    .removeHeader("User-Agent")
                    .header("User-Agent", HttpUtils.INSTANCE.getUserAgent())
                    .build();

            Response response = chain.proceed(request);
            return response.newBuilder()
                    .removeHeader("Pragma")
                    .removeHeader("Cache-Control")
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .build();
        }
  }
}
  • 请求中添加拦截
.addInterceptor(CacheInterceptor())

到此,缓存机制和okhttp配置缓存介绍完了,所有信息都是在Request和Response的Header中传输配置信息和判断信息,达到数据的交互,从而减少流量的消耗。

 

你可能感兴趣的:(网络请求)