HttpCache in android
Overview
http CacheIt refers to the ability of web browsers to reuse "copies" of locally cached documents. We know that getting content through the network is sometimes costly, so
Caching and reusing the previously acquired resources becomes a key aspect of optimizing performance. The http protocol provides cache support.
Cache advantages
1. reduce redundant data transmission 2. alleviate network bandwidth bottlenecks 3. reduce distance latency 4. Reduce server load
Http support for caching
Expires & Cache-Control
ExpiresThe Response Header provides an absolute response expiration time. In this way, a client such as a browser can cache a copy before the expiration time,
You don't have to ask whether the server is valid. Http1.0. Example: Expires: Thu, 03 Oct 1997 17:15:00 GMT
Cache-ControlThe header is used to transmit the cache information of an object. Http1.1. Its value is a cache command, which gives a result related to the cacheability of an object.
Cache specific commands. This header can appear in the request or response header. Example: Cache-Control: no-cache
ExpiresAndCache-ControlConsistent roles refer to the validity period of the current resource. It controls whether the browser directly retrieves data from the browser cache or resends the data.
Request data from the server.
Cache-ControlYou can set the following values:
(1) Request:
No-cache-do not read files in the cache and request a new request from the WEB server. In the http1.0 era, pragma: no-cache is available, which works the same way. no-store -- Request and response are forbidden to be cached max-age: -- indicates that the request will not be sent to the server again within seconds of the max-age after the webpage is accessed, its function is similar to Expires, except that Expires is compared based on a specific date value. Max-stale -- allows read of cache objects whose expiration time must be smaller than the max-stale value. Min-fresh -- accept the cached object whose max-age life period is greater than the sum of its current time and min-fresh value only-if-cached -- inform the cache maker that I want the content to come from the cache, I don't care whether the cached response is fresh. no-transform -- tell the proxy not to change the media type, such as jpg, to png.
(2) Response:
Public-the data content is stored, and even password-protected webpages are stored. The security is very low. private-the data content can only be stored in a private cache, which is only valid for a user, no-cache cannot be shared-it can be cached, but it can be returned to the client no-store only after it is verified with the WEB server. Both requests and responses are prohibited from being cached. max-age: -- The expiration time of the object contained in this response is Must-revalidate -- if the cache expires, the system checks whether the cache is the latest data with the original server, instead of the proxy max-stale in the middle -- allows the read of cache objects whose expiration time must be smaller than the max-stale value. The only difference between s-maxage and max-age is that s-maxage is only used for shared cache. instead, it should not be used for the local cache of the user proxy or other single-user cache. in addition, the priority of s-maxage is higher than that of max-age. no-transform -- tell the proxy not to change the media type, such as jpg, to png.
Note:ExpiresThe first value is absolute time, which is easily affected by the local clock deviation.Cache-ControlIs relative time. Therefore,Cache-ControlOf
Priority higherExpires
Optimal
Cache-ControlPolicy
Cache-Control policy title =>
ETag & If-None-Match
ETagNotifies the browser of the unique identifier of the current resource on the server, carried by the Response Header
If-None-MatchCheck whether the resource entity has changed and is carried by the Request Header
These two headers are usually used in combination. First, the browser requests a document resource. The server calculates the resource, generates the fingerprint (md5 or other...), and then uses the fingerprintETagToken
Put it in the Response HeaderETagSent to the browser. After receiving the response, the browser caches the resource. When the browser accesses this resource for the second time, it first looks for the resource from the cache,
AndETagTokenIf-None-MatchThe value of the request header is sent to the server. After the server obtains the request headerIf-None-MatchTheETag
Verify the token against the fingerprint of the resource. If the token does not change, 304 is returned, indicating that the browser can reuse the cache. Otherwise, the request is re-requested and based on the request result,
200 or 4xx is returned.
Last-Modified & if-Modified-Since
Last-ModifiedThe first provides the date on which the resource was last modified, carried by the Response Header
if-Modified-SinceThe first check whether the resource has been modified after this time, carried by the Request Header
These two headers are usually used in combination. First, the browser requests a document resource. The server finds the resource and adds it to the response header.Last-ModifiedIdentifies the resource closest
The time of one change. After the browser receives a response, it caches the resource. When the browser accesses this resource for the second time, it first looks for this resource from the cache andlast-Modified
Asif-Modified-SinceThe header value is sent to the server. After obtaining the request header, the server returns the date value of 'if-Modified-Since 'to the nearest resource.
Compare the change time. If the change time is the same, the system will directly return 304, indicating that the resource can be reused. Otherwise, the system will send a new request and return 200 or 4xx based on the request results. Note: No
All browsers supportif-Modified-SinceField
For example, if I access this image:
Http://img.alicdn.com/bao/uploaded/i4/2433767071/TB2hL7dfXXXXXaeXXXXXXXXXXXX !! 2433767071-0-w.mai.jpg
First request and response:
Second request and response: <喎?http: www.bkjia.com kf ware vc " target="_blank" class="keylink"> VcD4NCjxwPjxpbWcgYWx0PQ = "second request and Response" src = "http://www.bkjia.com/uploads/allimg/151112/04151G1H-2.png" title = "\"/>
The first request is not carried because there is no cached copy locally.if-Last-ModifiedRequest Header, and the server will pass the last modification time of the ResourceLast-Modified
The second request will be carried because a copy is cached.if-Last-ModifiedAnd the response code shows that the first time is 200, and the second time is 304.
It is "difficult" to distinguish between cache hits and missed ones, because http does not provide users with a way to distinguish whether the response is cache hit or accessed from the original server.
In both cases, the response code is 200 OK. The client has a way to determine whether the response comes from the cache, that is, to use the Date header to set the value of the Date header in the response
Compared with the current event, if the date value in the response is earlier, the client can usually consider this as a cache response, or the client can detect it through the Age header.
Cache response.
Httpcache solution in android
HttpResponseCacheClass, after android4.0.
HttpResponseCacheCompatible version, https://github.com/candrews/HttpResponseCache.
Directly use Square'sOkHttpAfter 4.0, google directlyOkhttpThe project is integrated into the android projectHttpUrlConnection.
In solution 1HttpResponseCacheIt also depends onOkhttpHowever, we cannot directly useOkhttpBecause it ishide.
Provided by androidHttpResponseCacheFor example.
My code demonstrates how to useHttpResponseCacheCache a network image,NetworkUtilsClass providesgetBitmapAndasyncGetBitmapMethod for obtaining
Network image:
Code 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; }
Here I use the client settingsPolicyTo determine whether to cache images. If not, Setno-cacheHeader.installCacheIfNeededMethod
Used to initialize the cache, which is actually called internallyHttpCache#install()Method:
Code 2: HttpCache # install ()
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; } data-snippet-id=ext.2119c294a5428b71b1001e58f12468cb data-snippet-saved=false data-csrftoken=RvI0acnw-08Q46livGiVM05M4uTurSwGW5Pk data-codota-status=done>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; }
I wrote a test case in MainActivity to test the results when the cache is allowed or the cache is not allowed. The uploaded images are from the Alibaba cdn image server,
The image expiration time is one year, so it can be cached.
Cache-Control:max-age=31536000Content-Type:image/jpegDate:Thu, 22 Oct 2015 05:41:18 GMTExpires:Fri, 21 Oct 2016 05:41:18 GMT
The test results are perfect. When cache is allowed, you only need to request the server for the first time. After the data is obtained, it will be cached to the local preset directory, and subsequent requests will go directly.
Cache.
Let's take a look at the cached data format. Here I preset the cache directory in the package name/cache/http/(For more information about the code, see HttpCache. java ).
Directory, pull out, found three files, one of which is called journal, obviously, this is usedDiskLruCacheClass. One of the other two files is 185458 bytes,
It must be this image. The content of another file opened with sublime is as follows:
http://img.alicdn.com/bao/uploaded/i4/2433767071/TB2hL7dfXXXXXaeXXXXXXXXXXXX_!!2433767071-0-paimai.jpgGET0HTTP/1.1 200 OK18Server: TengineContent-Type: image/jpegContent-Length: 185458Connection: keep-aliveDate: Thu, 22 Oct 2015 05:41:18 GMTLast-Modified: Sat, 12 Sep 2015 11:24:37 GMTExpires: Fri, 21 Oct 2016 05:41:18 GMTCache-Control: max-age=31536000Access-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: 1501380X-Cache: HIT TCP_MEM_HIT dirn:2:377253066X-Swift-SaveTime: Sun, 08 Nov 2015 03:15:26 GMTX-Swift-CacheTime: 30075952Timing-Allow-Origin: *X-Android-Sent-Millis: 1446993858366X-Android-Received-Millis: 1446993858403X-Android-Response-Source: NETWORK 200
This is actually a response header,X-Android-Response-SourceThe header is appended by othttp. You can determine whether the response data is obtained from the disk cache or
From the network. The Code is as follows:
Code 3:
// Return true indicates the static boolean parseResponseSourceHeader (String header) obtained from the cache {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 ;}}
PassparseResponseSourceHeader(connection.getHeaderField(X-Android-Response-Source))You can determine.
If you useOkHttpThe cache control is more convenient. You only need to createOkHttpClientSet the cache when the instance is running. For specific code, referOkHttpUtilsClass.
Code 4:
try { mHttpClient.setCache(new Cache(new File(context.getApplicationContext().getCacheDir(), CACHE_DIR), CACHE_SIZE)); } catch (Exception e) { }
The cache policy can be selected throughCacheControlTo control.
Code 5:
CacheControl cacheControl; if (policy == Policy.Cache) { cacheControl = CacheControl.FORCE_CACHE; } else { CacheControl.Builder builder = new CacheControl.Builder(); cacheControl = builder.noCache().build(); }
Http cache application
Open source libraries such as volley and picasso all use the http cache as the DiskCache solution. For specific code, see Volley # HttpHeaderParser class and
Picasso # URLConnectionDownloader class.