Android中okhttp3.4.1+retrofit2.1.0實(shí)現(xiàn)離線緩存
關(guān)于Retrofit+OkHttp的強(qiáng)大這里就不多說(shuō)了,還沒(méi)了解的同學(xué)可以自行去百度。這篇文章主要講如何利用Retrofit+OkHttp來(lái)實(shí)現(xiàn)一個(gè)較為簡(jiǎn)單的緩存策略:
即有網(wǎng)環(huán)境下我們請(qǐng)求數(shù)據(jù)時(shí),如果沒(méi)有緩存或者緩存過(guò)期了,就去服務(wù)器拿數(shù)據(jù),并且將新緩存保存下來(lái),如果有緩存而且沒(méi)有過(guò)期,則直接使用緩存。無(wú)網(wǎng)環(huán)境下我們請(qǐng)求數(shù)據(jù)時(shí),緩存沒(méi)過(guò)期則直接使用緩存,緩存過(guò)期了則無(wú)法使用,需要重新聯(lián)網(wǎng)獲取服務(wù)器數(shù)據(jù)。
緩存處理還是很有必要的,它有效的減少服務(wù)器負(fù)荷,降低延遲提升用戶體驗(yàn),同時(shí)也方便用戶即使在沒(méi)網(wǎng)絡(luò)的情況下也能使用APP。
之前一直有一個(gè)疑惑,既然Retrofit已經(jīng)是對(duì)OkHttp的一個(gè)封裝了,為什么還一直說(shuō)Retrofit+OkHttp要一起搭配使用,后來(lái)才知道其實(shí)OKHttp很重要的一個(gè)作用,就是對(duì)一些網(wǎng)絡(luò)請(qǐng)求的配置,例如連接超時(shí),讀取超時(shí),以及一些緩存配置等。
一、添加依賴
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
二、配置OkHttpClient(設(shè)置緩存路徑和緩存文件大小)
File httpCacheDirectory = new File(Environment.getExternalStorageDirectory(), "HttpCache");//這里為了方便直接把文件放在了SD卡根目錄的HttpCache中,一般放在context.getCacheDir()中 int cacheSize = 10 * 1024 * 1024;//設(shè)置緩存文件大小為10M Cache cache = new Cache(httpCacheDirectory, cacheSize); httpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS)//設(shè)置連接超時(shí) .readTimeout(10, TimeUnit.SECONDS)//讀取超時(shí) .writeTimeout(10, TimeUnit.SECONDS)//寫入超時(shí) .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)//添加自定義緩存攔截器(后面講解),注意這里需要使用.addNetworkInterceptor .cache(cache)//把緩存添加進(jìn)來(lái) .build();
三、配置Retrofit
retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .client(httpClient)//把OkHttpClient添加進(jìn)來(lái) .addConverterFactory(GsonConverterFactory.create()) .build();
四、編寫攔截器
我們知道其實(shí)Retrofit+OkHttp的緩存主要通過(guò)攔截器實(shí)現(xiàn),所以主要做的功夫也在攔截器里面。
static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); //網(wǎng)上很多示例代碼都對(duì)在request請(qǐng)求前對(duì)其進(jìn)行無(wú)網(wǎng)的判斷,其實(shí)無(wú)需判斷,無(wú)網(wǎng)自動(dòng)訪問(wèn)緩存 // if(!NetworkUtil.getInstance().isConnected()){ // request = request.newBuilder() // .cacheControl(CacheControl.FORCE_CACHE)//只訪問(wèn)緩存 // .build(); // } Response response = chain.proceed(request); if (NetworkUtil.getInstance().isConnected()) { int maxAge = 60;//緩存失效時(shí)間,單位為秒 return response.newBuilder() .removeHeader("Pragma")//清除頭信息,因?yàn)榉?wù)器如果不支持,會(huì)返回一些干擾信息,不清除下面無(wú)法生效 .header("Cache-Control", "public ,max-age=" + maxAge) .build(); } else { //這段代碼設(shè)置無(wú)效 // int maxStale = 60 * 60 * 24 * 28; // 無(wú)網(wǎng)絡(luò)時(shí),設(shè)置超時(shí)為4周 // return response.newBuilder() // .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) // .removeHeader("Pragma") // .build(); } return response; } };
到這里,其實(shí)已經(jīng)可以實(shí)現(xiàn)了我們開頭所說(shuō)的緩存效果了。
但是,上面設(shè)置的每個(gè)接口緩存時(shí)間都一樣,例如我現(xiàn)在想讓不同接口的緩存數(shù)據(jù)失效時(shí)間都不一樣,甚至有些接口不緩存數(shù)據(jù),應(yīng)該怎么做呢?其實(shí)也很簡(jiǎn)單
首先我們只需要在接口前面添加@Headers參數(shù)(max-age代表緩存時(shí)間,單位為秒,示例中表示緩存失效時(shí)間為60s,想要多少時(shí)間可以自行設(shè)置),不設(shè)置@Headers參數(shù)則不進(jìn)行緩存。
@Headers("Cache-Control:public ,max-age=60") @GET("getBusiness.action")//商店信息 Call<RestaurantInfoModel> getRestaurantInfo(@Query("userId") String userId,@Query("businessId") String businessId);
同時(shí),我們的緩存攔截器也要做下簡(jiǎn)單的修改(去掉了之前的注釋代碼)
static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); if (NetworkUtil.getInstance().isConnected()) { //獲取頭部信息 String cacheControl =request.cacheControl().toString(); return response.newBuilder() .removeHeader("Pragma")//清除頭信息,因?yàn)榉?wù)器如果不支持,會(huì)返回一些干擾信息,不清除下面無(wú)法生效 .header("Cache-Control", cacheControl) .build(); } return response; } };
*注意:
1.只能緩存Get請(qǐng)求的接口,不能緩存Post請(qǐng)求的接口
2.OkHttpClient需要用.addNetworkInterceptor添加緩存攔截器,不能使用.addInterceptor,也無(wú)需兩者同時(shí)使用。
3.此方法無(wú)需服務(wù)器端任何操作,適用于服務(wù)器端沒(méi)有其他緩存策略,如果服務(wù)器端有自己的緩存策略代碼應(yīng)該做相應(yīng)的修改,以適應(yīng)服務(wù)器端。
附上所有代碼:
/** * 簡(jiǎn)單封裝的Retroit初始化類 */ public class initRetrofit { private static String baseUrl = "http://202.171.212.154:8080/hh/"; private static OkHttpClient httpClient; private static Retrofit retrofit; public static Retrofit initRetrofit() { //緩存路徑和大小 File httpCacheDirectory = new File(Environment.getExternalStorageDirectory(), "HttpCache"); int cacheSize = 10 * 1024 * 1024; Cache cache = new Cache(httpCacheDirectory, cacheSize); //日志攔截器 HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); httpClient = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS)//設(shè)置連接超時(shí) .readTimeout(10, TimeUnit.SECONDS)//讀取超時(shí) .writeTimeout(10, TimeUnit.SECONDS)//寫入超時(shí) .addInterceptor(interceptor)//添加日志攔截器 .addNetworkInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)//添加緩存攔截器 .cache(cache)//把緩存添加進(jìn)來(lái) .build(); retrofit = new Retrofit.Builder() .baseUrl(baseUrl) .client(httpClient) .addConverterFactory(GsonConverterFactory.create()) .build(); return retrofit; } public static RetrofitAPI getService() { return initRetrofit().create(RetrofitAPI.class); } // //緩存攔截器,不同接口不同緩存 // static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() { // @Override // public Response intercept(Chain chain) throws IOException { // // Request request = chain.request(); // Response response = chain.proceed(request); // // if (NetworkUtil.getInstance().isConnected()) { // String cacheControl =request.cacheControl().toString(); // return response.newBuilder() // .removeHeader("Pragma")//清除頭信息,因?yàn)榉?wù)器如果不支持,會(huì)返回一些干擾信息,不清除下面無(wú)法生效 // .header("Cache-Control", cacheControl) // .build(); // } // return response; // } // }; //緩存攔截器,統(tǒng)一緩存60s static Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); Response response = chain.proceed(request); if (NetworkUtil.getInstance().isConnected()) { int maxAge = 60*60*24*2;//緩存失效時(shí)間,單位為秒 return response.newBuilder() .removeHeader("Pragma")//清除頭信息,因?yàn)榉?wù)器如果不支持,會(huì)返回一些干擾信息,不清除下面無(wú)法生效 .header("Cache-Control", "public ,max-age=" + maxAge) .build(); } return response; } }; }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android基礎(chǔ)之Fragment與Activity交互詳解
以下小編就為大家介紹一下Fragment跟Activity之間的關(guān)系。需要的朋友可以過(guò)來(lái)參考下2013-07-07第三方開源Android TickPlusDrawable狀態(tài)可以通過(guò)動(dòng)畫切換的按鈕
Android tickplusdrawable(TickPlusDrawable)是一個(gè)狀態(tài)可以通過(guò)動(dòng)畫切換的按鈕,本文給大家分享第三方開源Android TickPlusDrawable狀態(tài)可以通過(guò)動(dòng)畫切換的按鈕,感興趣的朋友一起學(xué)習(xí)吧2015-12-12android Socket實(shí)現(xiàn)簡(jiǎn)單聊天功能以及文件傳輸
這篇文章主要介紹了android Socket實(shí)現(xiàn)簡(jiǎn)單聊天功能以及文件傳輸,非常具有實(shí)用價(jià)值,有需要的朋友可以參考下。2017-02-02Android實(shí)現(xiàn)文件存儲(chǔ)案例
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)文件存儲(chǔ)案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11Android自定義控件案例匯總2(自定義開關(guān)、下拉刷新、側(cè)滑菜單)
這篇文章主要介紹了Android自定義控件案例匯總,自定義開關(guān)、Listview實(shí)現(xiàn)下拉刷新、側(cè)滑菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Android協(xié)程的7個(gè)重要知識(shí)點(diǎn)匯總
在現(xiàn)代Android應(yīng)用開發(fā)中,協(xié)程(Coroutine)已經(jīng)成為一種不可或缺的技術(shù),它不僅簡(jiǎn)化了異步編程,還提供了許多強(qiáng)大的工具和功能,可以在高階場(chǎng)景中發(fā)揮出色的表現(xiàn),本文將深入探討Coroutine重要知識(shí)點(diǎn),幫助開發(fā)者更好地利用Coroutine來(lái)構(gòu)建高效的Android應(yīng)用2023-09-09