欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

OKHttp3(支持Retrofit)的網(wǎng)絡(luò)數(shù)據(jù)緩存Interceptor攔截器的實現(xiàn)

 更新時間:2017年12月13日 10:30:09   作者:xiaolei123  
本篇文章主要介紹了OKHttp3(支持Retrofit)的網(wǎng)絡(luò)數(shù)據(jù)緩存Interceptor攔截器的實現(xiàn),具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言:前段時間在開發(fā)APP的時候,經(jīng)常出現(xiàn)由于用戶設(shè)備環(huán)境的原因,拿不到從網(wǎng)絡(luò)端獲取的數(shù)據(jù),所以在APP端展現(xiàn)的結(jié)果總是一個空白的框,這種情況對于用戶體驗來講是極其糟糕的,所以,苦思冥想決定對OKHTTP下手(因為我在項目中使用的網(wǎng)絡(luò)請求框架就是OKHTTP),則 寫了這么一個網(wǎng)絡(luò)數(shù)據(jù)緩存攔截器。
OK,那么我們決定開始寫了,我先說一下思路:

思路篇

既然要寫的是網(wǎng)絡(luò)數(shù)據(jù)緩存攔截器,主要是利用了OKHTTP強大的攔截器功能,那么我們應該對哪些數(shù)據(jù)進行緩存呢,或者在哪些情況下啟用數(shù)據(jù)進行緩存機制呢?

第一 :支持POST請求,因為官方已經(jīng)提供了一個緩存攔截器,但是有一個缺點,就是只能對GET請求的數(shù)據(jù)進行緩存,對POST則不支持。

第二 :網(wǎng)絡(luò)正常的時候,則是去網(wǎng)絡(luò)端取數(shù)據(jù),如果網(wǎng)絡(luò)異常,比如TimeOutException UnKnowHostException 諸如此類的問題,那么我們就需要去緩存取出數(shù)據(jù)返回。

第三 :如果從緩存中取出的數(shù)據(jù)是空的,那么我們還是需要讓這次請求走剩下的正常的流程。

第四 :調(diào)用者必須對緩存機制完全掌控,可以根據(jù)自己的業(yè)務(wù)需求選擇性的對數(shù)據(jù)決定是否進行緩存。

第五 :使用必須簡單,這是最最最最重要的一點。

好,我們上面羅列了五點是我們的大概思路,現(xiàn)在來說一下代碼部分:

代碼篇

緩存框架 :我這里使用的緩存框架是DiskLruCache https://github.com/JakeWharton/DiskLruCache 這個緩存框架可以存儲到本地,也經(jīng)過谷歌認可,這也是選擇這個框架的主要原因。我這里也對緩存框架進行封裝了一個CacheManager類:

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import com.xiaolei.OkhttpCacheInterceptor.Log.Log;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
 * Created by xiaolei on 2017/5/17.
 */
public class CacheManager
{
  public static final String TAG = "CacheManager";
  //max cache size 10mb
  private static final long DISK_CACHE_SIZE = 1024 * 1024 * 10;
  private static final int DISK_CACHE_INDEX = 0;
  private static final String CACHE_DIR = "responses";
  private DiskLruCache mDiskLruCache;
  private volatile static CacheManager mCacheManager;
  public static CacheManager getInstance(Context context)
  {
    if (mCacheManager == null)
    {
      synchronized (CacheManager.class)
      {
        if (mCacheManager == null)
        {
          mCacheManager = new CacheManager(context);
        }
      }
    }
    return mCacheManager;
  }

  private CacheManager(Context context)
  {
    File diskCacheDir = getDiskCacheDir(context, CACHE_DIR);
    if (!diskCacheDir.exists())
    {
      boolean b = diskCacheDir.mkdirs();
      Log.d(TAG, "!diskCacheDir.exists() --- diskCacheDir.mkdirs()=" + b);
    }
    if (diskCacheDir.getUsableSpace() > DISK_CACHE_SIZE)
    {
      try
      {
        mDiskLruCache = DiskLruCache.open(diskCacheDir,
            getAppVersion(context), 1/*一個key對應多少個文件*/, DISK_CACHE_SIZE);
        Log.d(TAG, "mDiskLruCache created");
      } catch (IOException e)
      {
        e.printStackTrace();
      }
    }
  }

  /**
   * 同步設(shè)置緩存
   */
  public void putCache(String key, String value)
  {
    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)
    {
      e.printStackTrace();
    } finally
    {
      if (os != null)
      {
        try
        {
          os.close();
        } catch (IOException e)
        {
          e.printStackTrace();
        }
      }
    }
  }

  /**
   * 異步設(shè)置緩存
   */
  public void setCache(final String key, final String value)
  {
    new Thread()
    {
      @Override
      public void run()
      {
        putCache(key, value);
      }
    }.start();
  }

  /**
   * 同步獲取緩存
   */
  public String getCache(String key)
  {
    if (mDiskLruCache == null)
    {
      return null;
    }
    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];
        int len;
        while ((len = fis.read(buf)) != -1)
        {
          bos.write(buf, 0, len);
        }
        byte[] data = bos.toByteArray();
        return new String(data);
      }
    } catch (IOException e)
    {
      e.printStackTrace();
    } finally
    {
      if (fis != null)
      {
        try
        {
          fis.close();
        } catch (IOException e)
        {
          e.printStackTrace();
        }
      }
      if (bos != null)
      {
        try
        {
          bos.close();
        } catch (IOException e)
        {
          e.printStackTrace();
        }
      }
    }
    return null;
  }

  /**
   * 異步獲取緩存
   */
  public void getCache(final String key, final CacheCallback callback)
  {
    new Thread()
    {
      @Override
      public void run()
      {
        String cache = getCache(key);
        callback.onGetCache(cache);
      }
    }.start();
  }

  /**
   * 移除緩存
   */
  public boolean removeCache(String key)
  {
    if (mDiskLruCache != null)
    {
      try
      {
        return mDiskLruCache.remove(encryptMD5(key));
      } catch (IOException e)
      {
        e.printStackTrace();
      }
    }
    return false;
  }

  /**
   * 獲取緩存目錄
   */
  private File getDiskCacheDir(Context context, String uniqueName)
  {
    String cachePath = context.getCacheDir().getPath();
    return new File(cachePath + File.separator + uniqueName);
  }

  /**
   * 對字符串進行MD5編碼
   */
  public static String 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));
      }
      return hex.toString();
    } catch (NoSuchAlgorithmException | UnsupportedEncodingException e)
    {
      e.printStackTrace();
    }
    return string;
  }

  /**
   * 獲取APP版本號
   */
  private int getAppVersion(Context context)
  {
    PackageManager pm = context.getPackageManager();
    try
    {
      PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
      return pi == null ? 0 : pi.versionCode;
    } catch (PackageManager.NameNotFoundException e)
    {
      e.printStackTrace();
    }
    return 0;
  }
}

緩存CacheInterceptor攔截器:利用OkHttp的Interceptor攔截器機制,智能判斷緩存場景,以及網(wǎng)絡(luò)情況,對不同的場景進行處理。

import android.content.Context;
import com.xiaolei.OkhttpCacheInterceptor.Catch.CacheManager;
import com.xiaolei.OkhttpCacheInterceptor.Log.Log;
import java.io.IOException;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

/**
 * 字符串的緩存類
 * Created by xiaolei on 2017/12/9.
 */
public class CacheInterceptor implements Interceptor
{
  private Context context;
  public void setContext(Context context)
  {
    this.context = context;
  }
  public CacheInterceptor(Context context)
  {
    this.context = context;
  }

  @Override
  public Response intercept(Chain chain) throws IOException
  {
    Request request = chain.request();
    String cacheHead = request.header("cache");
    String cache_control = request.header("Cache-Control");

    if ("true".equals(cacheHead) ||               // 意思是要緩存
        (cache_control != null && !cache_control.isEmpty())) // 這里還支持WEB端協(xié)議的緩存頭
    {
      long oldnow = System.currentTimeMillis();
      String url = request.url().url().toString();
      String responStr = null;
      String reqBodyStr = getPostParams(request);
      try
      {
        Response response = chain.proceed(request);
        if (response.isSuccessful()) // 只有在網(wǎng)絡(luò)請求返回成功之后,才進行緩存處理,否則,404存進緩存,豈不笑話
        {
          ResponseBody responseBody = response.body();
          if (responseBody != null)
          {
            responStr = responseBody.string();
            if (responStr == null)
            {
              responStr = "";
            }
            CacheManager.getInstance(context).setCache(CacheManager.encryptMD5(url + reqBodyStr), responStr);//存緩存,以鏈接+參數(shù)進行MD5編碼為KEY存
            Log.i("HttpRetrofit", "--> Push Cache:" + url + " :Success");
          }
          return getOnlineResponse(response, responStr);
        } else
        {
          return chain.proceed(request);
        }
      } catch (Exception e)
      {
        Response response = getCacheResponse(request, oldnow); // 發(fā)生異常了,我這里就開始去緩存,但是有可能沒有緩存,那么久需要丟給下一輪處理了
        if (response == null)
        {
          return chain.proceed(request);//丟給下一輪處理
        } else
        {
          return response;
        }
      }
    } else
    {
      return chain.proceed(request);
    }
  }

  private Response getCacheResponse(Request request, long oldNow)
  {
    Log.i("HttpRetrofit", "--> Try to Get Cache  --------");
    String url = request.url().url().toString();
    String params = getPostParams(request);
    String cacheStr = CacheManager.getInstance(context).getCache(CacheManager.encryptMD5(url + params));//取緩存,以鏈接+參數(shù)進行MD5編碼為KEY取
    if (cacheStr == null)
    {
      Log.i("HttpRetrofit", "<-- Get Cache Failure ---------");
      return null;
    }
    Response response = new Response.Builder()
        .code(200)
        .body(ResponseBody.create(null, cacheStr))
        .request(request)
        .message("OK")
        .protocol(Protocol.HTTP_1_0)
        .build();
    long useTime = System.currentTimeMillis() - oldNow;
    Log.i("HttpRetrofit", "<-- Get Cache: " + response.code() + " " + response.message() + " " + url + " (" + useTime + "ms)");
    Log.i("HttpRetrofit", cacheStr + "");
    return response;
  }

  private Response getOnlineResponse(Response response, String body)
  {
    ResponseBody responseBody = response.body();
    return new Response.Builder()
        .code(response.code())
        .body(ResponseBody.create(responseBody == null ? null : responseBody.contentType(), body))
        .request(response.request())
        .message(response.message())
        .protocol(response.protocol())
        .build();
  }

  /**
   * 獲取在Post方式下。向服務(wù)器發(fā)送的參數(shù)
   *
   * @param request
   * @return
   */
  private String getPostParams(Request request)
  {
    String reqBodyStr = "";
    String method = request.method();
    if ("POST".equals(method)) // 如果是Post,則盡可能解析每個參數(shù)
    {
      StringBuilder sb = new StringBuilder();
      if (request.body() instanceof FormBody)
      {
        FormBody body = (FormBody) request.body();
        if (body != null)
        {
          for (int i = 0; i < body.size(); i++)
          {
            sb.append(body.encodedName(i)).append("=").append(body.encodedValue(i)).append(",");
          }
          sb.delete(sb.length() - 1, sb.length());
        }
        reqBodyStr = sb.toString();
        sb.delete(0, sb.length());
      }
    }
    return reqBodyStr;
  }
}

以上是主體思路,以及主要實現(xiàn)代碼,現(xiàn)在來說一下使用方式

使用方式:

gradle使用:

compile 'com.xiaolei:OkhttpCacheInterceptor:1.0.0'

由于是剛剛提交到Jcenter,可能會出現(xiàn)拉不下來的情況(暫時還未過審核),著急的讀者可以再在你的Project:build.gradle里的repositories里新增我maven的鏈接:

allprojects {
  repositories {
    maven{url 'https://dl.bintray.com/kavipyouxiang/maven'}
  }
}

我們新建一個項目,項目截圖是這樣的:

項目截圖

demo很簡單,一個主頁面,一個Bean,一個Retrofit,一個網(wǎng)絡(luò)請求接口

注意,因為是網(wǎng)絡(luò),緩存,有關(guān),所以,毫無疑問我們要在manifest里面添加網(wǎng)絡(luò)請求權(quán)限,文件讀寫權(quán)限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

使用的時候,你只需要為你的OKHttpClient添加一個Interceptor:

client = new OkHttpClient.Builder()
        .addInterceptor(new CacheInterceptor(context))//添加緩存攔截器,添加緩存的支持
        .retryOnConnectionFailure(true)//失敗重連
        .connectTimeout(30, TimeUnit.SECONDS)//網(wǎng)絡(luò)請求超時時間單位為秒
        .build();

如果你想哪個接口的數(shù)據(jù)緩存,那么久為你的網(wǎng)絡(luò)接口,添加一個請求頭CacheHeaders.java這個類里包含了所有的情況,一般情況下只需要CacheHeaders.NORMAL就可以了

public interface Net
{
  @Headers(CacheHeaders.NORMAL) // 這里是關(guān)鍵
  @FormUrlEncoded
  @POST("geocoding")
  public Call<DataBean> getIndex(@Field("a") String a);
}

業(yè)務(wù)代碼:

Net net = retrofitBase.getRetrofit().create(Net.class);
    Call<DataBean> call = net.getIndex("蘇州市");
    call.enqueue(new Callback<DataBean>()
    {
      @Override
      public void onResponse(Call<DataBean> call, Response<DataBean> response)
      {
        DataBean data = response.body();
        Date date = new Date();
        textview.setText(date.getMinutes() + " " + date.getSeconds() + ":\n" + data + "");
      }

      @Override
      public void onFailure(Call<DataBean> call, Throwable t)
      {
        textview.setText("請求失?。?);
      }
    });

我們這里對網(wǎng)絡(luò)請求,成功了,則在界面上輸出文字,加上當前時間,網(wǎng)絡(luò)失敗,則輸出一個請求失敗。

大概代碼就是這樣子的,詳細代碼,文章末尾將貼出demo地址

看效果:演示圖

這里演示了,從網(wǎng)絡(luò)正常,到網(wǎng)絡(luò)不正常,再恢復到正常的情況。

結(jié)尾

以上篇章就是整個從思路,到代碼,再到效果圖的流程,這里貼一下DEMO的地址,喜歡的可以點個Start

Demo地址:https://github.com/xiaolei123/OkhttpCacheInterceptor

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java實現(xiàn)簡單汽車租賃系統(tǒng)

    java實現(xiàn)簡單汽車租賃系統(tǒng)

    這篇文章主要為大家詳細介紹了java實現(xiàn)簡單汽車租賃系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • Java中關(guān)于ThreadLocal的隱式引用詳解

    Java中關(guān)于ThreadLocal的隱式引用詳解

    這篇文章主要介紹了Java中關(guān)于ThreadLocal的隱式引用,從線程的角度看,每個線程都保持一個對其線程局部變量副本的隱式引用,只要線程是活動的,ThreadLocal實例就是可訪問的,下面我們來具體看看
    2024-03-03
  • Java使用EasyExcel進行單元格合并的問題詳解

    Java使用EasyExcel進行單元格合并的問題詳解

    項目中需要導出并合并指定的單元格,下面這篇文章主要給大家介紹了關(guān)于java評論、回復功能設(shè)計與實現(xiàn)的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-06-06
  • Java多態(tài)實現(xiàn)原理詳細梳理總結(jié)

    Java多態(tài)實現(xiàn)原理詳細梳理總結(jié)

    這篇文章主要介紹了Java多態(tài)實現(xiàn)原理詳細梳理總結(jié),多態(tài)是繼封裝、繼承之后,面向?qū)ο蟮牡谌筇匦?,本文只總結(jié)了多態(tài)的實現(xiàn)原理,需要的朋友可以參考一下
    2022-06-06
  • 分布式調(diào)度器之Spring Task 的使用詳解

    分布式調(diào)度器之Spring Task 的使用詳解

    SpringTask是Spring框架中用于任務(wù)調(diào)度的組件,通過簡單的注解就能實現(xiàn)定時任務(wù)的創(chuàng)建和調(diào)度,可以通過配置線程池來實現(xiàn),本文給大家介紹分布式調(diào)度器之Spring Task 的使用,感興趣的朋友跟隨小編一起看看吧
    2024-10-10
  • Java手把手必會的實例漢諾塔講解練習

    Java手把手必會的實例漢諾塔講解練習

    漢諾塔,傳說神在創(chuàng)造世界的時候做了三根金剛石柱子,并在一個教塔里留下了三根金剛石棒,第一根上面從上到下套著64個按從小到大排列的金盤,神命令廟里的眾僧將它們一個個地從這根金剛石棒搬到另一根金剛石棒上,大盤不能放在小盤上。最后64個金盤仍然要按從小到大排列
    2021-09-09
  • 使用Spring-Retry解決Spring Boot應用程序中的重試問題

    使用Spring-Retry解決Spring Boot應用程序中的重試問題

    重試的使用場景比較多,比如調(diào)用遠程服務(wù)時,由于網(wǎng)絡(luò)或者服務(wù)端響應慢導致調(diào)用超時,此時可以多重試幾次。用定時任務(wù)也可以實現(xiàn)重試的效果,但比較麻煩,用Spring Retry的話一個注解搞定所有,感興趣的可以了解一下
    2023-04-04
  • Java基礎(chǔ):流Stream詳解

    Java基礎(chǔ):流Stream詳解

    Stream流是數(shù)據(jù)渠道,用于操作數(shù)據(jù)源(集合、數(shù)組等)所生成的元素序列。這篇文章主要介紹了Java8新特性Stream流的相關(guān)資料,需要的朋友參考下吧
    2021-09-09
  • java實現(xiàn)高效的枚舉元素集合示例

    java實現(xiàn)高效的枚舉元素集合示例

    Set是Java集合類的重要組成部分,它用來存儲不能重復的對象。枚舉類型也要求其枚舉元素各不相同。看起來枚舉類型和集合是很相似的。然而枚舉類型中的元素不能隨意的增加、刪除,作為集合而言,枚舉類型非常不實用。EnumSet是專門為enum實現(xiàn)的集合類,本實例將演示其用法
    2014-03-03
  • 詳解使用Spring Security進行自動登錄驗證

    詳解使用Spring Security進行自動登錄驗證

    本篇文章主要介紹了詳解使用Spring Security進行自動登錄驗證,非常具有實用價值,需要的朋友可以參考下
    2017-09-09

最新評論