国产一区二区美女诱惑_国产精品免费播放_91精品国产综合久久香蕉麻豆 _久久精品30_久久综合88_国产精品亚洲成人_黑人极品videos精品欧美裸_亚洲色图欧美激情

原創生活

國內 商業 滾動

基金 金融 股票

期貨金融

科技 行業 房產

銀行 公司 消費

生活滾動

保險 海外 觀察

財經 生活 期貨

當前位置:滾動 >

Retrofit 和OkHttp 中使用網絡緩存,提高訪問效率

文章來源:財金網  發布時間: 2019-04-23 10:53:32  責任編輯:cfenews.com
+|-

【原標題:Retrofit 和OkHttp 中使用網絡緩存,提高訪問效率】財金網消息 OkHttp緩存原理

我們先從HTTP協議開始入手,關于緩存的HTTP請求/返回頭有以下幾個,我列了張表格一一解釋.

請求頭/返回頭含義

Cache-Control這個字段用于指定所有緩存機制在整個請求/響應鏈中

必須服從的指令。

Pragma與Cache-Control一樣,是兼容HTTP1.0的頭部

Expires資源過期時間

Last-Modified資源最后修改的時間

If-Modified-Since在請求頭中指定一個日期,若資源最后更新時間超過該日期,

則服務器接受請求,相反的頭為If-Unmodified-Since

ETag識別內容版本的唯一字符串,與資源關聯的記號

與緩存最相關的Cache-Control有多條指令,并且在請求或返回頭中的效果不一樣.

在請求頭中Cache-Control的指令

指令參數說明

no-cache無緩存必須向服務器確認是否過期候才能使用,

即不接受過期緩存,并非不緩存

no-store無真正意義上的不緩存

max-age=[秒]必須響應的最大age值

max-stale=[秒]可忽略可接受的最大過期時間

min-fresh=[秒]必須詢問再過[秒]時間后資源是否過期,若過期則不返回

only-if-cached無只獲取緩存的資源而不聯網獲取

在返回頭中Cache-Control的指令

指令參數說明

public無可向任意方提供響應的緩存

private無向特定用戶提供響應緩存

no-cache可省略不緩存

no-store無不緩存

max-age=[秒]必須響應的最大age值

max-stale=[秒]可忽略可接受的最大過期時間

min-fresh=[秒]必須詢問再過[秒]時間后資源是否過期,若過期則不返回

only-if-cached無只獲取緩存的資源而不聯網獲取

假設Okhttp完全遵守HTTP協議(實際上應該也是),利用Cache-Control我們可以緩存某些必要的資源.

有網絡的時候:短時間內頻繁的請求,后面的請求使用緩存中的資源.

無網絡的時候:獲取之前緩存的數據進行暫時的頁面顯示,當網絡更新時對當前activity的數據進行刷新,刷新界面,避免界面空白的場景.

編寫OkHttp網絡攔截器

class CacheNetworkInterceptor implements Interceptor {

public Response intercept(Interceptor.Chain chain) throws IOException {

//無緩存,進行緩存

return chain.proceed(chain.request()).newBuilder()

.removeHeader("Pragma")

//對請求進行最大60秒的緩存

.addHeader("Cache-Control", "max-age=60")

.build();

}

}

static class CacheInterceptor implements Interceptor {

public Response intercept(Interceptor.Chain chain) throws IOException {

Response resp;

Request req;

if (ok) {

//有網絡,檢查10秒內的緩存

req = chain.request()

.newBuilder()

.cacheControl(new CacheControl

.Builder()

.maxAge(10, TimeUnit.SECONDS)

.build())

.build();

} else {

//無網絡,檢查30天內的緩存,即使是過期的緩存

req = chain.request().newBuilder()

.cacheControl(new CacheControl.Builder()

.onlyIfCached()

.maxStale(30, TimeUnit.DAYS)

.build())

.build();

}

resp = chain.proceed(req);

return resp.newBuilder().build();

}

}

配置OKHTTP中的Cache

int cacheSize = 10 * 1024 * 1024; // 10 MiB

Cache cache = new Cache(httpCacheDirectory, cacheSize);

OkHttpClient client = new OkHttpClient.Builder()

.cache(cache)

//加入攔截器,注意Network與非Network的區別

.addInterceptor(new CacheInterceptor())

.addNetworkInterceptor(new CacheNetworkInterceptor())

.connectTimeout(10, TimeUnit.SECONDS)

.readTimeout(10, TimeUnit.SECONDS)

.build();

//最后通過使用該HTTP Client進行網絡請求, 就實現上述利用緩存優化應用的需求

在retrofit中使用只要將retrofit的okhttpclient換成這個帶緩存的okhttpclient即可

private val okhttpClient = OkHttpClient.Builder()

.connectTimeout(timeout, TimeUnit.MILLISECONDS)

.readTimeout(timeout, TimeUnit.MILLISECONDS)

.writeTimeout(timeout, TimeUnit.MILLISECONDS)

.retryOnConnectionFailure(true)

.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))

.addInterceptor(CacheInterceptor())

.addNetworkInterceptor(CacheNetworkInterceptor())

.cache(Cache(File(App.app.externalCacheDir, "ok-cache"), 1024 * 1024 * 30L))

.build()

var retrofit2 = Retrofit.Builder().baseUrl(baseURL)

.addCallAdapterFactory(RxJavaCallAdapterFactory.create())

.addConverterFactory(GsonConverterFactory.create())

.client(okhttpClient)

.build()

解釋一下上面的代碼,CacheInterceptor主要的作用是判斷當前網絡是否有效,如果有效,則創建一個請求.

該請求能獲取一個10秒內未過期的緩存,否則強制獲取一個緩存(過期了30天也允許).而CacheNetworkInterceptor主要是在緩存沒命中的情況下,請求網絡后,修改返回頭,加上Cache-Control,告知OKHTTP對該請求進行一個60秒的緩存.

因此,當頻繁請求的時候,OKHTTP使用10秒之內的緩存而不重復請求網絡.當沒網絡的時候,請求會獲取30天內的緩存,避免界面白屏.

OkHttp關于Cache的源碼分析

分析源碼之前先看下Cache的策略

Cache.png

Cache.png

Response getResponseWithInterceptorChain() throws IOException {

// Okhttp獲取Response的入口

// 采用責任鏈模式,一層層按順序轉交Request并處理Response

List interceptors = new ArrayList<>();

// 用戶定義的攔截器

interceptors.addAll(client.interceptors());

interceptors.add(retryAndFollowUpInterceptor);

interceptors.add(new BridgeInterceptor(client.cookieJar()));

//CacheInterceptor主要用于做緩存控制

interceptors.add(new CacheInterceptor(client.internalCache()));

interceptors.add(new ConnectInterceptor(client));

if (!forWebSocket) {

//用戶定義的Network攔截器

interceptors.addAll(client.networkInterceptors());

}

// 發起實際請求的攔截器

interceptors.add(new CallServerInterceptor(forWebSocket));

Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,

originalRequest, this, eventListener, client.connectTimeoutMillis(),

client.readTimeoutMillis(), client.writeTimeoutMillis());

return chain.proceed(originalRequest);

}

這里我們主要看CacheInterceptor的實現,CacheInterceptor代碼比較長,我們分段來解釋:

@Override public Response intercept(Chain chain) throws IOException {

Response cacheCandidate = cache != null

? cache.get(chain.request())

: null;

// 實際上是類似map,將返回內容的URL的MD5的值當key,返回內容當response

// 然后從cache文件里面查詢是否存在該緩存

long now = System.currentTimeMillis();

//根據當前的時間,以及緩存策略,來獲取response

CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();

Request networkRequest = strategy.networkRequest;

Response cacheResponse = strategy.cacheResponse;

// 根據策略得到cacheReposne 與 NetworkRequest

// 之后的代碼就是根據這兩個東西設置返回頭

// 不進行網絡請求,且緩存以及過期了,返回504錯誤

if (networkRequest == null && cacheResponse == null) {

return new Response.Builder()

.request(chain.request())

.protocol(Protocol.HTTP_1_1)

.code(504)

.message("Unsatisfiable Request (only-if-cached)")

.body(Util.EMPTY_RESPONSE)

.sentRequestAtMillis(-1L)

.receivedResponseAtMillis(System.currentTimeMillis())

.build();

}

// 不進行網絡請求,此時緩存命中,直接返回緩存,后面的攔截器也不會調用了

if (networkRequest == null) {

return cacheResponse.newBuilder()

.cacheResponse(stripBody(cacheResponse))

.build();

}

// 否則需要請求網絡,繼續調用責任鏈后面的攔截器,請求網絡并獲取response

Response networkResponse = null;

try {

networkResponse = chain.proceed(networkRequest);

} finally {

// 請求異常,關閉緩存避免泄漏

if (networkResponse == null && cacheCandidate != null) {

closeQuietly(cacheCandidate.body());

}

}

// 請求了網絡的同時,緩存其實也找到的情況

// (比如 需要向服務器確認緩存是否可用的情況)

if (cacheResponse != null) {

// 返回了304, 我們都知道304的返回時不帶body的,此時必須向獲取cache的body

if (networkResponse.code() == HTTP_NOT_MODIFIED) {

Response response = cacheResponse.newBuilder()

.headers(combine(cacheResponse.headers(), networkResponse.headers()))

.sentRequestAtMillis(networkResponse.sentRequestAtMillis())

.receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())

.cacheResponse(stripBody(cacheResponse))

.networkResponse(stripBody(networkResponse))

.build();

networkResponse.body().close();

// Update the cache after combining headers but before stripping the

// Content-Encoding header (as performed by initContentStream()).

cache.trackConditionalCacheHit();

cache.update(cacheResponse, response);

return response;

} else {

closeQuietly(cacheResponse.body());

}

}

//省略---------

}

// 緩存策略CacheStrategy主要的策略寫在該方法下

private CacheStrategy getCandidate() {

// 沒有緩存!

if (cacheResponse == null) {

return new CacheStrategy(request, null);

}

// 當請求的協議是https的時候,如果cache沒有hansake就丟棄緩存

if (request.isHttps() && cacheResponse.handshake() == null) {

return new CacheStrategy(request, null);

}

/// -- 省略一些代碼

// 根據緩存的緩存時間,緩存可接受最大過期時間等等HTTP協議上的規范

// 來判斷緩存是否可用,

if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {

Response.Builder builder = cacheResponse.newBuilder();

if (ageMillis + minFreshMillis >= freshMillis) {

builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");

}

long oneDayMillis = 24 * 60 * 60 * 1000L;

if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {

builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");

}

return new CacheStrategy(null, builder.build());

}

}

// 請求條件, 當etag,lastModified,servedDate這三種屬性存在時

//需要向服務器確認緩存的有效性

String conditionName;

String conditionValue;

if (etag != null) {

conditionName = "If-None-Match";

conditionValue = etag;

} else if (lastModified != null) {

conditionName = "If-Modified-Since";

conditionValue = lastModifiedString;

} else if (servedDate != null) {

conditionName = "If-Modified-Since";

conditionValue = servedDateString;

} else {

return new CacheStrategy(request, null); // 不存在的時候,按流程進行請求

}

Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();

Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);

// 構造一個請求詢問服務器資源是否過期

Request conditionalRequest = request.newBuilder()

.headers(conditionalRequestHeaders.build())

.build();

return new CacheStrategy(conditionalRequest, cacheResponse);

借用一張圖來說明http的整個工作流程

流程也很清晰明了了,簡單的說及時通過Request創建RealCall對象,經過層層interceptor之后最終產生一個response.

不過值得注意的是,當CacheInterceptor命中緩存之后, 后面的攔截器將不再執行.

這也是addInterceptor 與 addNetworkInterceptor之間的區別.

最后附上當網絡可用的時候,自動重新請求的一個基于MVP模式的實現方案

NetStatusMonitor是一個單例,用于監聽整個應用程序的網絡狀態.

ActivityManager也是一個單例,用來管理應用程序的活動棧,原理Application注冊關于活動的生命周期監聽.

基于MVP模式,給presenter的抽象基類定義一個refresh的方法,當斷網時間超過XX秒的時候,調用在棧頂的activity的presenter進行刷新頁面.

如有不足請各位大佬指正:

NetStatusMonitor.setNetStatusListener(object: NetStatusMonitor.Listener {

var lostTime = 0L

override fun onLost() {

lostTime = System.currentTimeMillis()

}

override fun onAvailable() {

with(ActivityManager.peek() as BaseView<*>){

//當棧頂活動位于前臺

if(this.lifecycle.currentState == Lifecycle.State.RESUMED){

// 獲取ForegroundActivity進行刷新

// 斷線時間超過30秒重連再刷新一次

if(System.currentTimeMillis() - lostTime > 1000 * 30){

// 通知presenter刷新數據

this.presenter.refresh()

}

}

}

}

override fun onNetStateChange(oldState: Int, newState: Int) {

if(newState == NetStatusMonitor.MOBILE){

showToast("正在使用移動網絡")

}

}

})

object NetStatusMonitor {

interface Listener{

fun onLost()

fun onAvailable()

fun onNetStateChange(oldState: Int, newState: Int)

}

val WIFI = 1;

val MOBILE = 2;

val WIFI_MOBILE = 3;

val UNKNOW = 0

var available = false

var netState: Int by Delegates.observable(UNKNOW) { property, oldValue, newValue ->

listener?.onNetStateChange(oldValue, newValue)

}

private var listener : Listener? = null

fun setNetStatusListener(listener: Listener){

this.listener = listener

}

init {

val cm = Utils.app.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

fun setType() {

val activeNetwork = cm.activeNetworkInfo

val isMobile = activeNetwork.type == ConnectivityManager.TYPE_MOBILE

val isWifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isAvailable

if (isWifi && isMobile)

netState = WIFI_MOBILE

else if (isWifi && !isMobile)

netState = WIFI

else if (isMobile && !isWifi)

netState = MOBILE

else

netState = UNKNOW

}

cm.requestNetwork(NetworkRequest.Builder().build(), object : ConnectivityManager.NetworkCallback() {

override fun onAvailable(network: Network?) {

available = true

setType()

listener?.onAvailable()

}

override fun onLost(network: Network?) {

available = false

listener?.onLost()

}

})

}

}

專題首頁|財金網首頁

原創
新聞

精彩
互動

獨家
觀察

京ICP備2021034106號-38   營業執照公示信息  財金網  版權所有  cfenews.com  投稿郵箱:362293157@qq.com  業務QQ:362293157立即發帖
国产一区二区在线观看视频| 欧美视频官网| 97影院手机在线观看| 国产精品女人毛片| 国产91亚洲精品久久久| 亚洲精品久久久一区二区三区| 色婷婷综合网| 高清电影在线观看免费| 91精品国产乱码久久蜜臀| 一区二区电影在线观看| 美足av综合网| 欧美人成免费网站| 欧美日本一区| 国产精品亚洲欧美一级在线| 亚欧精品一区| 国产欧美精品国产国产专区| 97精品在线| 伦xxxx在线| 91精品国产一区二区三区香蕉| 懂色中文一区二区在线播放| 国产精品99视频| 亚洲色图图片| 无线免费在线视频| 精品女厕一区二区三区| 国产综合自拍| 亚洲国产精品嫩草影院久久av| 伊人影院蕉久影院在线播放| 白天操夜夜操| 亚洲人成在线观看一区二区| 精品国产99| 精精国产xxxx视频在线野外| 欧美日韩一级视频| 奇米一区二区三区| 欧美在线播放| 亚洲影院天堂中文av色| 日韩久久一区| 老司机2019福利精品视频导航| 精品成人佐山爱一区二区| 成人的网站免费观看| 久久一区欧美| 麻豆视频一区| 粉嫩av一区二区三区四区五区 | caoporn97在线视频| 亚洲国产精品va在线看黑人| 欧美艳星brazzers| 欧美国产精品一区二区| 91麻豆文化传媒在线观看| 国产精品1区2区3区| 天使萌一区二区三区免费观看| 欧美精品日韩| 成人在线超碰| 久久人人爽人人爽人人片av不| 嗯啊主人调教在线播放视频| 一二三四中文在线| 天堂在线观看一卡二卡三卡四卡| 日韩精品亚洲精品| 中文字幕第12页| 日韩精品亚洲视频| 中文字幕在线观| h网站视频在线观看| 免费男女羞羞的视频网站主页在线观看| 日韩理论片久久| 亚洲精品黄网在线观看| 日韩免费高清av| 亚洲区中文字幕| 亚洲国产另类 国产精品国产免费| 欧美成人欧美edvon| 日韩av在线免费观看| 免费在线看污| 日韩精品视频无播放器在线看| 国产九九在线| 成人免费在线观看视频| 成人影院天天5g天天爽无毒影院 | 欧美—级在线免费片| 91亚洲大成网污www| 亚洲一区在线观看视频| 色婷婷久久久久swag精品| 亚洲国产91精品在线观看| 男女网站在线观看| 亚洲国产aⅴ精品一区二区三区| 美女毛片一区二区三区四区| 国产精品一在线观看| 老司机一区二区三区| 亚洲黄一区二区三区| 色婷婷综合久久久久中文一区二区 | 1024国产在线| 成人日韩在线观看| 日韩精品水蜜桃| 91视频一区二区| 午夜欧美在线一二页| 高清日韩av| 亚洲综合av一区二区三区| 国产亚洲精品美女久久| 日本网站在线观看一区二区三区| 亚洲天堂免费在线观看视频| 亚洲欧美中文字幕在线一区| 黄色片在线看| 欧美电影免费看| 久久精品综合| 在线观看欧美黄色| 91破解版在线看| 91蜜桃臀久久一区二区 | 欧美在线小视频| 免费av在线网站| 日韩一区二区免费看| 日本一区二区三区四区在线视频 | 91色视频在线| 蜜桃专区在线| 欧美1区3d| 亚洲品质自拍视频网站| 三级毛片在线免费看| 欧美a级片一区| 欧美在线观看视频一区二区| 欧美日本一道| av一区二区高清| 亚洲精品综合在线| 快色在线观看| 亚洲综合小说| 欧美精品在线观看播放| www在线视频| 日本在线不卡视频一二三区| 一区二区三区中文在线| 国产美女在线播放| 成人爽a毛片免费啪啪红桃视频| 久久久久国产精品免费免费搜索| avlululu| 亚洲国产综合在线看不卡| 亚洲成人自拍一区| 日韩免费福利视频| 亚洲日本在线视频观看| av手机免费在线观看| 粉嫩久久99精品久久久久久夜 | 捆绑调教美女网站视频一区| 欧美三级日韩三级国产三级| 精品一区二区三区免费看| 亚洲一二三专区| 亚洲小说春色综合另类电影| 国产午夜精品一区二区| 松下纱荣子在线观看| 亚洲女厕所小便bbb| 视频精品一区二区三区| 2021中文字幕一区亚洲| 色婷婷在线播放| 成人欧美一区二区三区1314| 国产电影一区二区三区爱妃记| av电影一区二区| wwww在线观看免费视频| 亚洲激情图片qvod| 国产欧美自拍| 欧美视频一区二区三区| 欧美成人日韩| 污视频在线看操| 国产欧美精品一区aⅴ影院| 日韩黄色在线| 亚洲国产精彩中文乱码av在线播放| 亚洲精品黑牛一区二区三区| 日韩免费一区二区| 久久成人免费电影| 欧美1级2级| 日韩精品视频在线观看网址| 牛夜精品久久久久久久99黑人| 欧美女子与性| 亚洲成在人线免费| 亚洲精品亚洲人成在线| 在线播放你懂的| 亚洲自拍另类综合| 欧美成人综合| av在线日韩国产精品| 亚洲国产精品久久人人爱| 雨宫琴音一区二区三区| 欧美人与性动交α欧美精品济南到| 欧美日韩午夜剧场| 日本女优在线视频一区二区| 亚洲国产精选| 国产91久久久久蜜臀青青天草二| 国产精品免费观看视频| 一区二区三区高清在线观看| 中文字幕在线资源| 日韩欧美中文免费| 国产伦精品一区二区三区在线观看 | 亚洲视频一二区| 日本在线一区二区三区| 欧美综合亚洲图片综合区| 欧美肥老太太性生活| 日本蜜桃在线观看| 尤物在线观看一区| 亚洲国产精品一区制服丝袜| 国产私拍福利精品视频二区| 欧美一区国产二区| 久久99精品久久久久| 欧美极品免费| 九色视频网站入口| 亚洲国产高清在线| 亚洲男女自偷自拍| 韩国理伦片久久电影网| 福利视频在线播放| 日韩激情在线视频| 欧美精品色一区二区三区| 久久精品国产精品亚洲红杏 | 亚洲一区日本|