1、前言
距離上一文RxHttp 讓你眼前一亮的Http請求框架的發表,已過去兩週,文章一經發表,RxHttp就收穫了衆多的好評,Github上Star數,也正式突破了1000,這對於我來說,是非常有動力的事情,感謝大家的支持,我再接再厲。
也有很多人跟說我,希望RxHttp能支持協程和緩存這兩大功能,這不,非常好用的緩存功能就來了,看完本文,相信你會再一次愛上RxHttp。另外,協程也已經在路上了,這個還需要點時間,大家耐心等待。
如果你喜歡RxHttp,歡迎進QQ羣交流:378530627 (RxHttp&RxLife交流羣)
2、設置全局緩存策略
通過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/RxHttpCache
maxSize
:緩存最大Size,最大爲10M,超過這個size,內部會根據LRU算法,將最少使用的緩存自動清除
CacheMode
:全局緩存模式,這裏爲,先請求網絡,失敗後,再讀取緩存
cacheVaildTime
:全局緩存有效時長,爲60秒
其中第三個參數,就是設置全局緩存模式。
RxHttp內部共提供了5種緩存模式,如下:
-
ONLY_NETWORK
該模式下,僅請求網絡,不處理緩存;這是RxHttp默認的緩存模式
-
ONLY_CACHE
該模式下,僅讀取緩存,讀取成功,直接返回;否則直接拋出異常
-
NETWORK_SUCCESS_WRITE_CACHE
該模式下,直接請求網絡,若請求成功,則寫入緩存並返回;否則直接返回
-
READ_CACHE_FAILED_REQUEST_NETWORK
該模式下,先讀取緩存,讀取成功,直接返回;否則將請求網絡(請求成功,寫入緩存)
-
REQUEST_NETWORK_FAILED_READ_CACHE
該模式下,先請求網絡,請求成功,寫入緩存並返回;否則讀取緩存
3、設置單個請求的緩存策略
通過Rxhttp#setCache(CacheMode)
爲單個請求設置單獨的緩存策略,如下:
RxHttp.get("/service/...")
.setCacheValidTime(10 * 1000) //當前請求緩存有效期爲10秒
.setCache(CacheMode.READ_CACHE_FAILED_REQUEST_NETWORK) //當前請求先讀取緩存,失敗後,再請求網絡
.asString()
.subscribe(s -> {
//成功回調
}, throwable -> {
//失敗回調
});
以上代碼,爲單個請求設置了單獨的緩存策略,其中有效時長爲10秒,緩存模式爲先讀取緩存,失敗後,再請求網絡。如果不設置,將使用全局的緩存策略。
注:設置單獨的緩存模式/緩存有效時長前,一定要設置緩存存儲目錄和緩存最大Size,否則無效
4、關於CacheKey
CacheKey在RxHttp的緩存模塊中,是一個很重要的角色,內部緩存的讀寫、刪除都是通過CacheKey來操作的。對於不相同的請求,要保證CacheKey的唯一性
;對於相同的請求,要保證CacheKey的靜態性
,這兩個特性非常重要,一定要保證,否則緩存將變得毫無意義。
爲啥這麼說,請往下看。
4.1、內部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()
方法查看
4.2、自定義CacheKey
如果以上規則不能滿足你的業務需求,RxHttp還提供了兩種自定義CacheKey的方式,如下:
1、通過RxHttp#setCacheKey(String)
方法,爲單個請求指定CacheKey
RxHttp.get("/service/...")
.setCacheKey("自定義的CacheKey") //自定義CacheKey
.asString()
.subscribe(s -> {
//成功回調
}, throwable -> {
//失敗回調
});
2、通過自定義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的唯一性,若重複,將覆蓋已存在的緩存
4.3、唯一性
唯一性,就是要保證不同的請求都具有不同的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的緩存,這就是我說的緩存錯亂
。
4.4、靜態性
何爲靜態性?我對它的定義是:同一個請求,發送多次,要保證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都是一樣的,這就是我說的靜態性
5、擴展
到這,也許有人會問我,我有這麼一種業務場景:打開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個:
1、如果直接支持的話,就必須要增加一個緩存讀取成功的回調,這樣會破壞RxHttp現有的代碼結構
2、個人感覺,組合的方式,比起增加一個緩存讀取成功的回調,代碼更加的簡潔,學習成本更低
當然,對於直接支持,後續如果想到更好的方案,一定會加入,如果你有好方案,記得分享。
6、警告
重要的事情說3遍
在使用緩存功能前,一定要搞懂,緩存模式、CacheKey的唯一性和靜態性
在使用緩存功能前,一定要搞懂,緩存模式、CacheKey的唯一性和靜態性
在使用緩存功能前,一定要搞懂,緩存模式、CacheKey的唯一性和靜態性
7、小結
其實,前面講了這麼做,RxHttp緩存功能的使用只需要2步的,
- 第一步,設置一個緩存存儲目錄和緩存最大Size,如果需要,還可以設置全局緩存模式和緩存有效時長
- 第二步,發請求時,設置對應的緩存模式即可,如果使用全局緩存模式,這一步還可以跳過
最後,喜歡的,請給本文點個贊,如果可以,還請給個star,創作不易,感激不盡。🙏🙏🙏
協程正在路上,請耐心等待。。