素材巴巴 > 程序开发 >

HttpCache in android

程序开发 2023-09-03 19:26:40

github地址:https://github.com/Rowandjj/AndroidHttpCache

概述

http Cache指的是web浏览器所具有的复用本地已缓存的文档”副本”的能力。我们知道,通过网络获取内容有时候成本很高,因而
缓存和重用以前获取的资源的能力成为优化性能很关键的一个方面。http协议本身提供了缓存的支持。

缓存的优势

1. 减少冗余数据传输
 2. 缓解网络带宽瓶颈
 3. 降低距离时延
 4. 减少服务器负担
 

http协议对缓存的支持

(2)Response:

注意:Expires首部的值是绝对时间,容易受本地时钟偏差影响,而Cache-Control是相对时间。因而,Cache-Control
优先级高于Expires

区分缓存命中还是未命中有点”困难”,因为http没有为用户提供一种手段来区分响应是缓存命中的,还是访问原始服务器得到的。
在这两种情况下,响应码都是200 ok。客户端有一种方法可以判断响应是否来自缓存,就是使用Date首部,将响应中Date首部的值
和当前事件进行比较,如果响应中的日期值比较早,客户端通常就可以认为这是一条缓存的响应,或者客户端也可以通过Age首部来检测
缓存的响应。

android下的httpcache方案

以android提供的HttpResponseCache为例说明。
我的代码演示了如何使用HttpResponseCache缓存一张网络图片,NetworkUtils类提供了getBitmapasyncGetBitmap方法,用于获取
网络图片:

代码1:NetworkUtils#getBitmap()

public static HttpResponse getBitmap(Context context,Uri uri,Policy policy){if(uri == null){throw new IllegalArgumentException("uri is null");}installCacheIfNeeded(context);try {HttpURLConnection connection = (HttpURLConnection) new URL(MainActivity.URL).openConnection();connection.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);connection.setReadTimeout(DEFAULT_READ_TIMEOUT);if(policy == Policy.Cache){connection.setUseCaches(true);}else{connection.setUseCaches(false);connection.addRequestProperty("Cache-Control", "no-cache");connection.addRequestProperty("Cache-Control","max-age=0");}int contentLen = connection.getContentLength();int responseCode = connection.getResponseCode();HttpResponse response = new HttpResponse(responseCode,null,contentLen, BitmapFactory.decodeStream(connection.getInputStream()));connection.getInputStream().close();return response;} catch (IOException e) {e.printStackTrace();}return null;}

这里面我通过客户端的设定Policy来判断是否需要缓存图片,如果不需要就设置no-cache头。installCacheIfNeeded方法
用于初始化缓存,内部实际上调用了HttpCache#install()方法:

代码2:HttpCache#install()

static HttpResponseCache install(Context context) {if (context == null) {throw new IllegalArgumentException("context must not be null");}File cacheDir = new File(context.getApplicationContext().getCacheDir(), CACHE_DIR);if (!cacheDir.exists()) {cacheDir.mkdirs();}HttpResponseCache cache = null;if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {try {if ((cache = HttpResponseCache.getInstalled()) != null) {return cache;}cache = HttpResponseCache.install(cacheDir, CACHE_SIZE);} catch (IOException e) {e.printStackTrace();Log.e(TAG, "http response installation failed,code = 0");}} else {try {if ((cache = (HttpResponseCache) Class.forName("android.net.http.HttpResponseCache").getMethod("getInstalled").invoke(null)) != null) {return cache;}Method method = Class.forName("android.net.http.HttpResponseCache").getMethod("install", File.class, long.class);cache = (HttpResponseCache) method.invoke(null, cacheDir, CACHE_SIZE);} catch (Exception e) {e.printStackTrace();Log.e(TAG, "http response installation failed,code = 1");}}return cache;}

我在MainActivity中编写了一个测试用例来分别测试了允许缓存和不允许缓存情况下的结果。加载的图片来自阿里cdn图片服务器,
这里的图片的过期时间是一年,所以可以确保能够缓存。

Cache-Control:max-age=31536000
 Content-Type:image/jpeg
 Date:Thu, 22 Oct 2015 05:41:18 GMT
 Expires:Fri, 21 Oct 2016 05:41:18 GMT

测试结果很完美,在允许缓存的情况下,只需要第一次请求server,拿到数据后会缓存到本地预设的目录,以后的请求都会直接走
缓存.

demo

我们来观察下缓存的数据格式,这里我预先设定了缓存目录位于包名/cache/http/下(相关代码参考HttpCache.java),ddms打开这个
目录,pull出来,发现三个文件,其中有个叫journal的,很显然,这是使用了DiskLruCache类,另外两个文件有一个大小是185458字节,
肯定是这张图片,另一个文件用sublime打开后内容如下:

http://img.alicdn.com/bao/uploaded/i4/2433767071/TB2hL7dfXXXXXaeXXXXXXXXXXXX_!!2433767071-0-paimai.jpg
 GET
 0
 HTTP/1.1 200 OK
 18
 Server: Tengine
 Content-Type: image/jpeg
 Content-Length: 185458
 Connection: keep-alive
 Date: Thu, 22 Oct 2015 05:41:18 GMT
 Last-Modified: Sat, 12 Sep 2015 11:24:37 GMT
 Expires: Fri, 21 Oct 2016 05:41:18 GMT
 Cache-Control: max-age=31536000
 Access-Control-Allow-Origin: *
 Via: cache7.l2cm12[0,200-0,H], cache60.l2cm12[16,0], cache1.cn406[0,200-0,H], cache8.cn406[1,0]
 Age: 1501380
 X-Cache: HIT TCP_MEM_HIT dirn:2:377253066
 X-Swift-SaveTime: Sun, 08 Nov 2015 03:15:26 GMT
 X-Swift-CacheTime: 30075952
 Timing-Allow-Origin: *
 X-Android-Sent-Millis: 1446993858366
 X-Android-Received-Millis: 1446993858403
 X-Android-Response-Source: NETWORK 200

这其实是一个响应头,X-Android-Response-Source首部是由othttp追加的,可以判断响应数据是从disk cache拿的还是
从网络拿的。代码如下:
代码3:

//返回true说明从缓存拿的
 static boolean parseResponseSourceHeader(String header) {if (header == null) {return false;}String[] parts = header.split(" ", 2);if ("CACHE".equals(parts[0])) {return true;}if (parts.length == 1) {return false;}try {return "CONDITIONAL_CACHE".equals(parts[0]) && Integer.parseInt(parts[1]) == 304;} catch (NumberFormatException e) {return false;}}
 

通过

parseResponseSourceHeader(connection.getHeaderField("X-Android-Response-Source"))即可判定.

如果使用OkHttp的话,缓存的控制更加方便,只需创建OkHttpClient实例的时候设置缓存即可。具体代码可以参考OkHttpUtils类.

代码4:

 try {mHttpClient.setCache(new Cache(new File(context.getApplicationContext().getCacheDir(), CACHE_DIR), CACHE_SIZE));} catch (Exception e) {}

缓存的策略选择可通过CacheControl来控制。

代码5:

  CacheControl cacheControl;if (policy == Policy.Cache) {cacheControl = CacheControl.FORCE_CACHE;} else {CacheControl.Builder builder = new CacheControl.Builder();cacheControl = builder.noCache().build();}

http cache的应用

像volley、picasso等开源库都利用了http缓存来作为DiskCache的方案。具体代码可参见Volley#HttpHeaderParser类以及
Picasso#URLConnectionDownloader类等.

参考资料:

  1. HTTP协议探索之Cache-Control
  2. HTTP 缓存
  3. HttpResponseCache

联系我:

如果有描述错误或不准确的,欢迎微博私信(@楚奕RJ)


标签:

上一篇: 获取免费天气(Java抓取百度天气) 下一篇:
素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。