距離上一文RxHttp 讓你眼前一亮的Http請求框架的發表,已過去兩週,文章一經發表,RxHttp就收穫了衆多的好評,Github上Star數,也正式突破了1000,這對於我來講,是很是有動力的事情,感謝你們的支持,我再接再礪。java
也有不少人跟說我,但願RxHttp能支持協程和緩存這兩大功能,這不,很是好用的緩存功能就來了,看完本文,相信你會再一次愛上RxHttp。另外,協程也已經在路上了,這個須要多一點的時間,你們耐心等待。git
若是你喜歡RxHttp,歡迎進QQ羣交流:378530627 (RxHttp&RxLife交流羣)github
gradle依賴算法
implementation 'com.rxjava.rxhttp:rxhttp:1.3.6'
//註解處理器,生成RxHttp類,便可一條鏈發送請求
annotationProcessor 'com.rxjava.rxhttp:rxhttp-compiler:1.3.6'
//管理RxJava及生命週期,Activity/Fragment 銷燬,自動關閉未完成的請求
implementation 'com.rxjava.rxlife:rxlife:1.1.0'
//非必須 根據本身需求選擇Converter RxHttp默認內置了GsonConverter
implementation 'com.rxjava.rxhttp:converter-jackson:1.3.6'
implementation 'com.rxjava.rxhttp:converter-fastjson:1.3.6'
implementation 'com.rxjava.rxhttp:converter-protobuf:1.3.6'
implementation 'com.rxjava.rxhttp:converter-simplexml:1.3.6'
複製代碼
注:kotlin用戶,請使用kapt替代annotationProcessor
json
經過RxHttpPlugins.setCache(File, long, CacheMode, long)
靜態方法,設置全局緩存策略,以下:緩存
public void initRxHttpCahce(Concent contet) {
//設置緩存目錄爲:Android/data/{app包名目錄}/cache/RxHttpCache
File cacheDir = new File(context.getExternalCacheDir(), "RxHttpCache");
//設置最大緩存爲10M,緩存有效時長爲60秒
RxHttpPlugins.setCache(cacheDir, 10 * 1024 * 1024, CacheMode.REQUEST_NETWORK_FAILED_READ_CACHE, 60 * 1000);
}
複製代碼
以上代碼,經過setCache
方法,配置瞭如下4個參數:網絡
cacheDir
:緩存存儲目錄,路徑爲:Android/data/{app包名目錄}/cache/RxHttpCacheapp
maxSize
:緩存最大Size,最大爲10M,超過這個size,內部會根據LRU算法,將最少使用的緩存自動清除框架
CacheMode
:全局緩存模式,這裏爲,先請求網絡,失敗後,再讀取緩存ide
cacheVaildTime
:全局緩存有效時長,爲60秒
其中第三個參數,就是設置全局緩存模式。
RxHttp內部共提供了5種緩存模式,以下:
ONLY_NETWORK
該模式下,僅請求網絡,不處理緩存;這是RxHttp默認的緩存模式
ONLY_CACHE
該模式下,僅讀取緩存,讀取成功,直接返回;不然直接拋出異常
NETWORK_SUCCESS_WRITE_CACHE
該模式下,直接請求網絡,若請求成功,則寫入緩存並返回;不然直接返回
READ_CACHE_FAILED_REQUEST_NETWORK
該模式下,先讀取緩存,讀取成功,直接返回;不然將請求網絡(請求成功,寫入緩存)
REQUEST_NETWORK_FAILED_READ_CACHE
該模式下,先請求網絡,請求成功,寫入緩存並返回;不然讀取緩存
經過Rxhttp#setCache(CacheMode)
爲單個請求設置單獨的緩存策略,以下:
RxHttp.get("/service/...")
.setCacheValidTime(10 * 1000) //當前請求緩存有效期爲10秒
.setCache(CacheMode.READ_CACHE_FAILED_REQUEST_NETWORK) //當前請求先讀取緩存,失敗後,再請求網絡
.asString()
.subscribe(s -> {
//成功回調
}, throwable -> {
//失敗回調
});
複製代碼
以上代碼,爲單個請求設置了單獨的緩存策略,其中有效時長爲10秒,緩存模式爲先讀取緩存,失敗後,再請求網絡。若是不設置,將使用全局的緩存策略。
注:設置單獨的緩存模式/緩存有效時長前,必定要設置緩存存儲目錄和緩存最大Size,不然無效
CacheKey在RxHttp的緩存模塊中,是一個很重要的角色,內部緩存的讀寫、刪除都是經過CacheKey來操做的。對於不相同的請求,要保證CacheKey的惟一性
;對於相同的請求,要保證CacheKey的靜態性
,這兩個特性很是重要,必定要保證,不然緩存將變得毫無心義。
爲啥這麼說,請往下看。
RxHttp內部會根據必定規則,爲每一個請求,都生成一個具備惟一性的CacheKey,規則以下:
NoBodyParam:
將添加的參數拼接在url後面,如:CacheKey = url?key=value&key1=value1... ,Get、Head類型請求遵循這個規則
FormParam:
同NoBodyParam,將添加的參數拼接在url後面,如:CacheKey = url?key=value&key1=value1... , xxxForm類型請求遵循這個規則
JsonParam:
將添加的參數轉換成Json字符串jsonStr後,添加到url後面,如:CacheKey = url?json=jsonStr , xxxJson類型請求遵循這個規則
JsonArrayParam:
同JsonParam,將添加的參數轉換成Json字符串jsonStr後,添加到url後面,如:CacheKey = url?json=jsonStr, xxxJsonArray類型請求遵循這個規則
以上4個規則,可在對應的Param類中getCacheKey()
方法查看
若是以上規則不能知足你的業務需求,RxHttp還提供了兩種自定義CacheKey的方式,以下:
一、經過RxHttp#setCacheKey(String)
方法,爲單個請求指定CacheKey
RxHttp.get("/service/...")
.setCacheKey("自定義的CacheKey") //自定義CacheKey
.asString()
.subscribe(s -> {
//成功回調
}, throwable -> {
//失敗回調
});
複製代碼
二、經過自定義Param,並重寫getCacheKey()方法,爲某一類請求制定CacheKey的生成規則,以下:
@Param(methodName = "postEncryptForm")
public class PostEncryptFormParam extends FormParam {
public PostEncryptFormParam(String url) {
super(url, Method.POST);
}
@Override
public String getCacheKey() {
String cacheKey = null;
String simpleUrl = getSimpleUrl(); //拿到Url
Headers headers = getHeaders();//拿到添加的請求頭
List<KeyValuePair> keyValuePairs = getKeyValuePairs(); //拿到添加的參數
cacheKey = "根據url/請求頭/參數,自定義規則,生成CacheKey";
return cacheKey;
}
}
複製代碼
注:自定義CacheKey,必定要保證CacheKey的惟一性,若重複,將覆蓋已存在的緩存
惟一性,就是要保證不一樣的請求都具備不一樣的CacheKey。若相同,就會形成緩存錯亂
。
舉個例子:
有A、B兩個不一樣的請求,CacheKey、緩存模式均同樣,緩存模式爲READ_CACHE_FAILED_REQUEST_NETWORK
,即先讀緩存,失敗後,再請求網絡。
此時,A先發請求,步驟爲:讀緩存失敗—>請求網絡—>請求成功,寫入緩存—>結束;
隨後B發起請求,步驟爲:讀緩存成功—>結束;
由於A和B的CacheKey是同樣的,因此B讀緩存時,讀到的就是A的。並且,若是B的緩存模式爲REQUEST_NETWORK_FAILED_READ_CACHE,此時,若是B請求成功,寫緩存時,由於CacheKey同樣,就會把A的緩存覆蓋掉,下次A讀取緩存時,讀的就是B的緩存,這就是我說的緩存錯亂
。
何爲靜態性?我對它的定義是:同一個請求,發送屢次,要保證CacheKey是一致的,不然,緩存將毫無心義。
試想,咱們有這樣一種狀況,每次發送請求,都要帶上當前的時間戳,以下:
RxHttp.get("/service/...")
.add("currentTime", System.currentTimeMillis())
.setCache(CacheMode.READ_CACHE_FAILED_REQUEST_NETWORK) //當前請求先讀取緩存,失敗後,再請求網絡
.asString()
.subscribe(s -> {
//成功回調
}, throwable -> {
//失敗回調
});
複製代碼
上面的代碼,每次發起請求,時間戳都不同,就致使內部每次生成的CacheKey也不同,從而形成每次都會寫入相同的緩存而不覆蓋,更嚴重的是,每次讀取緩存都會失敗,那咱們如何避免這種狀況呢?有,前面介紹的,自定義Cachekey就能解決這個問題,那還有沒有更簡單的辦法呢?也有,往下看。
剔除不參與CacheKey組拼的參數
咱們能夠調用RxHttpPlugins.setExcludeCacheKeys(String... keys)
方法,設置不參與CacheKey組拼的參數,即剔除這些參數。對於上面的問題,就能夠這麼作:
RxHttpPlugins.setExcludeCacheKeys("currentTime") //可變參數,可傳入多個key
複製代碼
此時,發送請求,當前時間戳就不會參與CacheKey的組拼,從而保證了每次發起請求,CacheKey都是同樣的,這就是我說的靜態性
到這,也許有人會問我,我有這麼一種業務場景:打開app,列表先展現緩存的數據,隨後再請求網絡,拉取最新的數據,用RxHttp如何實現?
對於這種場景,咱們須要這樣一種緩存模式,即:先讀取緩存,無論成功與否,都繼續請求網絡。然而,RxHttp的5中緩存模式中,沒有這樣一種模式,怎麼辦?既然提出來了,確定有辦法,經過兩種模式的組合來實現這個場景,以下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
//發送一個僅讀取緩存的請求
requestData(CacheMode.ONLY_CACHE);
//接着在發送一個請求網絡,成功後寫緩存的請求
requestData(CacheMode.NETWORK_SUCCESS_WRITE_CACHE);
}
//獲取數據
public void requestData(CacheMode cacheMode) {
RxHttp.get("/service/...")
.setCacheMode(cacheMode)
.asString()
.as(RxLife.asOnMain(this)) //感知生命週期,並在主線程回調
.subscribe(s -> {
//成功回調
if (cacheMode == CacheMode.ONLY_CACHE) {
//緩存讀取成功回調
} else if (cacheMode == CacheMode.NETWORK_SUCCESS_WRITE_CACHE) {
//請求網絡成功回調
}
}, throwable -> {
});
}
}
複製代碼
上面代碼中,咱們發送了兩個請求,兩個請求分別爲CacheMode.ONLY_CACHE
、CacheMode.NETWORK_SUCCESS_WRITE_CACHE
這兩種緩存模式,隨後,在成功回調裏,根據緩存模式的不一樣,執行不一樣的業務邏輯便可。
那RxHttp爲何不增長一種緩存模式,直接支持這種業務場景呢?緣由有2個:
一、若是直接支持的話,就必需要增長一個緩存讀取成功的回調,這樣會破壞RxHttp現有的代碼結構
二、我的感受,組合的方式,比起增長一個緩存讀取成功的回調,代碼更加的簡潔,學習成本更低
固然,對於直接支持,後續若是想到更好的方案,必定會加入,若是你有好方案,記得分享。
其實,前面講了這麼作,RxHttp緩存功能的使用只須要2步的,
最後,喜歡的,請給本文點個贊,若是能夠,還請給個star,創做不易,感激涕零。🙏🙏🙏
協程正在路上,須要多一點的時間,請耐心等待。。