Cache-Control
HTTPä¸è¿ä¸ªå段ç¨äºæå®ææç¼åæºå¶å¨æ´ä¸ªè¯·æ±/ååºé¾ä¸å¿ é¡»æä»çæ令ãç¼åæ令æ¯ååçï¼å³è¯·æ±ä¸åå¨ä¸ä¸ªæ令并ä¸æå³çååºä¸å°åå¨åä¸ä¸ªæ令ã常è§çåå¼æprivateãno-cacheãmax-ageãmust-revalidateçï¼é»è®¤ä¸ºprivateã
å¸¸ç¨ cache-directive å¼
Cache-directive | 说æ |
public | ææå 容é½å°è¢«ç¼å(客æ·ç«¯å代çæå¡å¨é½å¯ç¼å) |
private | å 容åªç¼åå°ç§æç¼åä¸(ä» å®¢æ·ç«¯å¯ä»¥ç¼åï¼ä»£çæå¡å¨ä¸å¯ç¼å) |
no-cache | å¿ é¡»å ä¸æå¡å¨ç¡®è®¤è¿åçååºæ¯å¦è¢«æ´æ¹ï¼ç¶åæè½ä½¿ç¨è¯¥ååºæ¥æ»¡è¶³åç»å¯¹åä¸ä¸ªç½åç请æ±ãå æ¤ï¼å¦æåå¨åéçéªè¯ä»¤ç (ETag)ï¼no-cache ä¼åèµ·å¾è¿éä¿¡æ¥éªè¯ç¼åçååºï¼å¦æèµæºæªè¢«æ´æ¹ï¼å¯ä»¥é¿å ä¸è½½ã |
no-store | ææå 容é½ä¸ä¼è¢«ç¼åå°ç¼åæ Internet 临æ¶æä»¶ä¸ |
must-revalidation/proxy-revalidation | å¦æç¼åçå 容失æï¼è¯·æ±å¿ é¡»åéå°æå¡å¨/代ç以è¿è¡éæ°éªè¯ |
max-age=xxx (xxx is numeric) | ç¼åçå 容å°å¨ xxx ç§å失æ, è¿ä¸ªé项åªå¨HTTP 1.1å¯ç¨, 并å¦æåLast-Modifiedä¸èµ·ä½¿ç¨æ¶, ä¼å 级è¾é« |
Retrofitå®ç°ç½ç»ç¼å
é¦å ï¼é ç½®OkHttpä¸Cache
OkHttpClient okHttpClient = new OkHttpClient();
File cacheFile = new File(context.getCacheDir(), "[ç¼åç®å½]");
Cache cache = new Cache(cacheFile, 1024 * 1024 * 100);//100Mb
okHttpClient.setCache(cache);
é 置请æ±å¤´ä¸çCache-Control
@FormUrlEncoded
   @Headers("Cache-Control: public,max-age:3600")
   @POST("?method=app.system.init")
   Observable<HttpResult<AppInit>>getAppInit(@Field("ucode") String ucode);
äºç«¯é å设置ååºå¤´æè èªå·±åæ¦æªå¨ä¿®æ¹ååºå¤´responseä¸cache-control
å°è¿ä¸æ¥ç¼å就已ç»å¾ å¨ä½ çç¼åç®å½äºã
å¦æäºç«¯æå¤écacheçè¯ï¼å°±å·²ç»å¯ä»¥äºã
ä½æ¯å¾å¯è½äºç«¯æ²¡æå¤çï¼æ以è¿åçååºå¤´ä¸cache-controlæ¯no-cacheï¼è¿æ¶åä½ è¿æ¯æ æ³åç¼åï¼å¤§å®¶å¯ä»¥ç¨okhttpçåæ¥å¿æ¦æªå¨æ¥çååºå¤´çå 容ã
å¦æäºç«¯ç°å¨ä¸æ¹ä¾¿å¤ççè¯ï¼ä½ ä¹å¯ä»¥èªå·±æå®ç¼åçï¼é£å°±æ¯åæ¦æªå¨ä¿®æ¹ååºå¤´ä¸çcache-controlã
设置æ¦æªå¨ï¼
CacheControlInterceptor cacheControlInterceptor = newCacheControlInterceptor();
return new OkHttpClient
               .Builder()
               .addInterceptor(loggingInterceptor)
               .addInterceptor(paramsInterceptor)
               .addInterceptor(cacheInterceptor)
               .cache(cache)
               .build();
æ¦æªå¨ç代ç å¦ä¸ï¼
public class CacheControlInterceptor implementsInterceptor {
Â
   @Override
   public Responseintercept(Chain chain) throws IOException {
       CacheControl.Builder cacheBuilder = new CacheControl.Builder();
       cacheBuilder.maxAge(0, TimeUnit.SECONDS);
       cacheBuilder.maxStale(365, TimeUnit.DAYS);
       CacheControl cacheControl = cacheBuilder.build();
       Requestrequest = chain.request();
       if(!NetworkStateUtils.isNetworkAvailable()) {
           request= request.newBuilder()
                   .cacheControl(cacheControl)
                   .build();
       }
       ResponseoriginalResponse = chain.proceed(request);
       if(NetworkStateUtils.isNetworkAvailable()) {
           intmaxAge = 0; // read from cache
           returnoriginalResponse.newBuilder()
                   .removeHeader("Pragma")
                   .header("Cache-Control", "public ,max-age=" +maxAge)
                   .build();
       } else {
           intmaxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
           returnoriginalResponse.newBuilder()
                   .removeHeader("Pragma")
                   .header("Cache-Control", "public, only-if-cached,max-stale=" + maxStale)
                   .build();
       }
   }
Â
Retrofitæ¯æç¼åpost请æ±
ä¸è¿ï¼å 为RetrofitåOkHttpæ¯ä»¥æ¯æRestfulAPI为åæçï¼æ以ï¼åªå¯¹get请æ±ç¼åãå¦ææå¡å¨ä¸æ¯æ åçRestfulAPIï¼æ¯å¦å ¨é¨éç¨post请æ±ï¼é£ä¹å¦ä½å®ç°ç¼åå¢ï¼
Retrofitæ¬èº«çç¼åæ¯éè¿DiskLRUCacheå®ç°çï¼æ们å¯ä»¥ä»¿ç §å®æ¥å®ç°èªå·±çç¼åæ¥æ¯æpost请æ±ã
é¦å ï¼å¼å ¥DiskLRUCache
åä¸ä¸ªå·¥å ·ç±»æ¥è®¾ç½®åè·åç¼åï¼
public final class CacheManager {
Â
   public staticfinal String TAG = "CacheManager";
Â
   //max cachesize 100mb
   private static final long DISK_CACHE_SIZE =1024 * 1024 * 100;
Â
   private staticfinal int DISK_CACHE_INDEX = 0;
Â
   private staticfinal String CACHE_DIR = "responses";
   privatevolatile static CacheManager mCacheManager;
   privateDiskLruCache mDiskLruCache;
Â
   privateCacheManager() {
       FilediskCacheDir = getDiskCacheDir(App.getContext(), CACHE_DIR);
       if(!diskCacheDir.exists()) {
           booleanb = diskCacheDir.mkdirs();
           Log.d(TAG, "!diskCacheDir.exists() --- diskCacheDir.mkdirs()="+ b);
       }
       if(diskCacheDir.getUsableSpace() > DISK_CACHE_SIZE) {
           try {
               mDiskLruCache = DiskLruCache.open(diskCacheDir,
                       getAppVersion(App.getContext()), 1, DISK_CACHE_SIZE);
               Log.d(TAG, "mDiskLruCache created");
           } catch(IOException e) {
               e.printStackTrace();
           }
       }
   }
Â
   public staticCacheManager getInstance() {
       if (mCacheManager == null) {
           synchronized (CacheManager.class) {
               if(mCacheManager == null) {
                   mCacheManager = new CacheManager();
               }
           }
       }
       returnmCacheManager;
   }
Â
  Â
   private staticString encryptMD5(String string) {
       try {
           byte[]hash = MessageDigest.getInstance("MD5").digest(
                   string.getBytes("UTF-8"));
           StringBuilder hex = new StringBuilder(hash.length * 2);
           for(byte b : hash) {
               if((b & 0xFF) < 0x10) {
                   hex.append("0");
               }
               hex.append(Integer.toHexString(b & 0xFF));
           }
           returnhex.toString();
       } catch(NoSuchAlgorithmException | UnsupportedEncodingException e) {
           e.printStackTrace();
       }
       returnstring;
   }
Â
  Â
   public voidputCache(String key, String value) throws IOException {
       if(mDiskLruCache == null) return;
       OutputStream os = null;
       try {
           DiskLruCache.Editor editor = mDiskLruCache.edit(encryptMD5(key));
           os = editor.newOutputStream(DISK_CACHE_INDEX);
           os.write(value.getBytes());
           os.flush();
           editor.commit();
           mDiskLruCache.flush();
       } catch(IOException e) {
           throwe;
       } finally {
           if (os != null) {
               try{
                   os.close();
               }catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
   }
Â
  Â
   public StringgetCache(String key) throws IOException {
       if(mDiskLruCache == null) {
           returnnull;
       }
       FileInputStream fis = null;
       ByteArrayOutputStream bos = null;
       try {
           DiskLruCache.Snapshot snapshot = mDiskLruCache.get(encryptMD5(key));
           if(snapshot != null) {
               fis= (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);
               bos= new ByteArrayOutputStream();
               byte[] buf = new byte[1024];
               intlen;
               while ((len = fis.read(buf)) != -1) {
                   bos.write(buf, 0, len);
               }
               byte[] data = bos.toByteArray();
               return new String(data);
           }
       } catch(IOException e) {
           throwe;
       } finally {
           if (fis!= null) {
               try{
                   fis.close();
               }catch (IOException e) {
                   e.printStackTrace();
               }
           }
           if (bos!= null) {
               try{
                   bos.close();
               }catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
       return"";
   }
Â
  Â
   public booleanremoveCache(String key) {
       if(mDiskLruCache != null) {
           try {
               return mDiskLruCache.remove(encryptMD5(key));
           } catch(IOException e) {
               e.printStackTrace();
           }
       }
       returnfalse;
   }
Â
  Â
   private FilegetDiskCacheDir(Context context, String uniqueName) {
       StringcachePath = context.getCacheDir().getPath();
       return new File(cachePath + File.separator +uniqueName);
   }
Â
  Â
   private intgetAppVersion(Context context) {
       PackageManager pm = context.getPackageManager();
       try {
           PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
           returnpi == null ? 0 : pi.versionCode;
       } catch(PackageManager.NameNotFoundException e) {
           e.printStackTrace();
       }
       return 0;
   }
}
å®ç°ä¸ä¸ªEnhancedCacheInterceptorï¼æ¦æªpost并ç¼åpost请æ±ãå®ç°æ ç½ç»æ¶è¯»åç¼åã
public class EnhancedCacheInterceptor implementsInterceptor {
   @Override
   public Responseintercept(Chain chain) throws IOException {
       LogUtils.e("EnhancedCacheInterceptor");
       Requestrequest = chain.request();
Â
       HttpUrlhttpUrl = request.url();
       String url= httpUrl.toString();
       RequestBodyrequestBody = request.body();
       Charsetcharset = Charset.forName("UTF-8");
       StringBuilder sb = new StringBuilder();
       sb.append(httpUrl.queryParameter("method"));
      Â
       Log.e(CacheManager.TAG, "EnhancedCacheInterceptor -> key:"+ sb.toString());
Â
       if(NetworkStateUtils.isNetworkAvailable()) {
           Response response = chain.proceed(request);
           CacheControl cacheControl = request.cacheControl();
           LogUtils.e(cacheControl.toString());
           LogUtils.e("response: " + response.toString());
           LogUtils.e("responseBody : " + response.body().toString());
           if(!cacheControl.noStore()) {
               ResponseBody responseBody = response.body();
               MediaType contentType = responseBody.contentType();
Â
               BufferedSource source = responseBody.source();
               source.request(Long.MAX_VALUE);
               Buffer buffer = source.buffer();
Â
               if(contentType != null) {
                   charset = contentType.charset(Charset.forName("UTF-8"));
               }
               String key = sb.toString();
               //æå¡å¨è¿åçjsonåå§æ°æ®
               String json = buffer.clone().readString(charset);
Â
               CacheManager.getInstance().putCache(key, json);
               Log.e(CacheManager.TAG, "putcache-> key:" + key + "-> json:" + json);
           }
           returnresponse;
       } else {
           CacheControl cacheControl = request.cacheControl();
           if(!cacheControl.noStore()) {
               request = request.newBuilder()
                       .cacheControl(CacheControl.FORCE_CACHE)
                       .build();
               String key = sb.toString();
               String cache = CacheManager.getInstance().getCache(key);
               Response originalResponse =chain.proceed(request);
Â
               return originalResponse
                       .newBuilder()
                       .code(200)
                       .message("OK")
                       .body(ResponseBody.create(originalResponse.body().contentType(), cache))
                       .build();
           } else{
               return chain.proceed(request);
           }
       }
   }
}
注æï¼é»è®¤å¨æ ç½ç»æ åµä¸ï¼ä¼è¿å504 errorãè¿éhack responseï¼å¼ºå¶æ¹å200ï¼å¹¶è¿åç¼åæ°æ®ã
Â