一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - OKHttp3(支持Retrofit)的網絡數據緩存Interceptor攔截器的實現

OKHttp3(支持Retrofit)的網絡數據緩存Interceptor攔截器的實現

2021-03-04 09:58xiaolei123 Java教程

本篇文章主要介紹了OKHttp3(支持Retrofit)的網絡數據緩存Interceptor攔截器的實現,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言:前段時間在開發app的時候,經常出現由于用戶設備環境的原因,拿不到從網絡端獲取的數據,所以在app端展現的結果總是一個空白的框,這種情況對于用戶體驗來講是極其糟糕的,所以,苦思冥想決定對okhttp下手(因為我在項目中使用的網絡請求框架就是okhttp),則 寫了這么一個網絡數據緩存攔截器
ok,那么我們決定開始寫了,我先說一下思路:

思路篇

既然要寫的是網絡數據緩存攔截器,主要是利用了okhttp強大的攔截器功能,那么我們應該對哪些數據進行緩存呢,或者在哪些情況下啟用數據進行緩存機制呢?

第一 :支持post請求,因為官方已經提供了一個緩存攔截器,但是有一個缺點,就是只能對get請求的數據進行緩存,對post則不支持。

第二 :網絡正常的時候,則是去網絡端取數據,如果網絡異常,比如timeoutexception unknowhostexception 諸如此類的問題,那么我們就需要去緩存取出數據返回。

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

第四 :調用者必須對緩存機制完全掌控,可以根據自己的業務需求選擇性的對數據決定是否進行緩存。

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

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

代碼篇

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
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();
      }
    }
  }
 
  /**
   * 同步設置緩存
   */
  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();
        }
      }
    }
  }
 
  /**
   * 異步設置緩存
   */
  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攔截器機制,智能判斷緩存場景,以及網絡情況,對不同的場景進行處理。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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端協議的緩存頭
    {
      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()) // 只有在網絡請求返回成功之后,才進行緩存處理,否則,404存進緩存,豈不笑話
        {
          responsebody responsebody = response.body();
          if (responsebody != null)
          {
            responstr = responsebody.string();
            if (responstr == null)
            {
              responstr = "";
            }
            cachemanager.getinstance(context).setcache(cachemanager.encryptmd5(url + reqbodystr), responstr);//存緩存,以鏈接+參數進行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); // 發生異常了,我這里就開始去緩存,但是有可能沒有緩存,那么久需要丟給下一輪處理了
        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));//取緩存,以鏈接+參數進行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方式下。向服務器發送的參數
   *
   * @param request
   * @return
   */
  private string getpostparams(request request)
  {
    string reqbodystr = "";
    string method = request.method();
    if ("post".equals(method)) // 如果是post,則盡可能解析每個參數
    {
      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;
  }
}

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

使用方式:

gradle使用:

?
1
compile 'com.xiaolei:okhttpcacheinterceptor:1.0.0'

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

?
1
2
3
4
5
allprojects {
  repositories {
    maven{url 'https://dl.bintray.com/kavipyouxiang/maven'}
  }
}

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

OKHttp3(支持Retrofit)的網絡數據緩存Interceptor攔截器的實現

項目截圖

demo很簡單,一個主頁面,一個bean,一個retrofit,一個網絡請求接口

注意,因為是網絡,緩存,有關,所以,毫無疑問我們要在manifest里面添加網絡請求權限,文件讀寫權限:

?
1
2
3
<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:

?
1
2
3
4
5
client = new okhttpclient.builder()
        .addinterceptor(new cacheinterceptor(context))//添加緩存攔截器,添加緩存的支持
        .retryonconnectionfailure(true)//失敗重連
        .connecttimeout(30, timeunit.seconds)//網絡請求超時時間單位為秒
        .build();

如果你想哪個接口的數據緩存,那么久為你的網絡接口,添加一個請求頭cacheheaders.java這個類里包含了所有的情況,一般情況下只需要cacheheaders.normal就可以了

?
1
2
3
4
5
6
7
public interface net
{
  @headers(cacheheaders.normal) // 這里是關鍵
  @formurlencoded
  @post("geocoding")
  public call<databean> getindex(@field("a") string a);
}

業務代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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("請求失敗!");
      }
    });

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

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

看效果:演示圖

OKHttp3(支持Retrofit)的網絡數據緩存Interceptor攔截器的實現

這里演示了,從網絡正常,到網絡不正常,再恢復到正常的情況。

結尾

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

demo地址:https://github.com/xiaolei123/okhttpcacheinterceptor

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.jianshu.com/p/0c57d4856be1

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美日韩一区二区综合 | 爱情岛永久成人免费网站 | 国产日产精品久久久久快鸭 | 国产aaa伦理片 | 我的妹妹最近有点怪免费播放 | japaneseles女同专区 | 三级无删减高清在线影院 | 成年男女免费大片在线观看 | 欧美日韩亚洲成人 | 日韩精品亚洲一级在线观看 | 星星动漫无删减在线观看 | 范冰冰好紧好滑好湿 | 亲爱的客栈第二季免费观看完整版 | 91色在线观看国产 | 暖暖影院日本版 | 欧美性白人顶级hd | 免费一级特黄特色大片 | 日本漫画被黄漫免费动 | 男生操女生动态图 | 国产精品美女久久久久 | 国人精品视频在线观看 | 激情影院免费 | 成人操 | 成人免费体验区福利云点播 | 天天做天天爱天天爽综合区 | 99热最新 | 日本视频二区 | 韩国男女做性全过程视频 | 含羞草传媒每天免费一次破解 | 国产小视频网站 | 插鸡视频在线观看 | 99久久久久国产精品免费 | 小兰被扒开内裤露出p | 国产在线三级 | 国产不卡视频一区二区在线观看 | 嫩草影院地址一地址二 | 亚洲成人影院在线观看 | 日本哺乳期网站xxxx | 日不卡| 高清男的插曲女的 欢迎你老狼 | 91寡妇天天综合久久影院 |