RxHttp 全網Http緩存最優解

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種緩存模式,如下:

  1. ONLY_NETWORK

    該模式下,僅請求網絡,不處理緩存;這是RxHttp默認的緩存模式

  2. ONLY_CACHE

    該模式下,僅讀取緩存,讀取成功,直接返回;否則直接拋出異常

  3. NETWORK_SUCCESS_WRITE_CACHE

    該模式下,直接請求網絡,若請求成功,則寫入緩存並返回;否則直接返回

  4. READ_CACHE_FAILED_REQUEST_NETWORK

    該模式下,先讀取緩存,讀取成功,直接返回;否則將請求網絡(請求成功,寫入緩存)

  5. 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_CACHECacheMode.NETWORK_SUCCESS_WRITE_CACHE這兩種緩存模式,隨後,在成功回調裏,根據緩存模式的不同,執行不同的業務邏輯即可。

那RxHttp爲什麼不增加一種緩存模式,直接支持這種業務場景呢?原因有2個:

1、如果直接支持的話,就必須要增加一個緩存讀取成功的回調,這樣會破壞RxHttp現有的代碼結構

2、個人感覺,組合的方式,比起增加一個緩存讀取成功的回調,代碼更加的簡潔,學習成本更低

當然,對於直接支持,後續如果想到更好的方案,一定會加入,如果你有好方案,記得分享。

6、警告

重要的事情說3遍

在使用緩存功能前,一定要搞懂,緩存模式、CacheKey的唯一性和靜態性

在使用緩存功能前,一定要搞懂,緩存模式、CacheKey的唯一性和靜態性

在使用緩存功能前,一定要搞懂,緩存模式、CacheKey的唯一性和靜態性

7、小結

其實,前面講了這麼做,RxHttp緩存功能的使用只需要2步的,

  • 第一步,設置一個緩存存儲目錄和緩存最大Size,如果需要,還可以設置全局緩存模式和緩存有效時長
  • 第二步,發請求時,設置對應的緩存模式即可,如果使用全局緩存模式,這一步還可以跳過

最後,喜歡的,請給本文點個贊,如果可以,還請給個star,創作不易,感激不盡。🙏🙏🙏

協程正在路上,請耐心等待。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章