RxHttp 全網Http緩存最優解

一、前言

距離上一文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替代annotationProcessorjson

二、設置全局緩存策略

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

  1. ONLY_NETWORK

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

  2. ONLY_CACHE

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

  3. NETWORK_SUCCESS_WRITE_CACHE

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

  4. READ_CACHE_FAILED_REQUEST_NETWORK

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

  5. 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

CacheKey在RxHttp的緩存模塊中,是一個很重要的角色,內部緩存的讀寫、刪除都是經過CacheKey來操做的。對於不相同的請求,要保證CacheKey的惟一性;對於相同的請求,要保證CacheKey的靜態性,這兩個特性很是重要,必定要保證,不然緩存將變得毫無心義。

爲啥這麼說,請往下看。

4.一、內部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.二、自定義CacheKey

若是以上規則不能知足你的業務需求,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的惟一性,若重複,將覆蓋已存在的緩存

4.三、惟一性

惟一性,就是要保證不一樣的請求都具備不一樣的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.四、靜態性

何爲靜態性?我對它的定義是:同一個請求,發送屢次,要保證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_CACHECacheMode.NETWORK_SUCCESS_WRITE_CACHE這兩種緩存模式,隨後,在成功回調裏,根據緩存模式的不一樣,執行不一樣的業務邏輯便可。

那RxHttp爲何不增長一種緩存模式,直接支持這種業務場景呢?緣由有2個:

一、若是直接支持的話,就必需要增長一個緩存讀取成功的回調,這樣會破壞RxHttp現有的代碼結構

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

固然,對於直接支持,後續若是想到更好的方案,必定會加入,若是你有好方案,記得分享。

六、小結

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

  • 第一步,設置一個緩存存儲目錄和緩存最大Size,若是須要,還能夠設置全局緩存模式和緩存有效時長
  • 第二步,發請求時,設置對應的緩存模式便可,若是使用全局緩存模式,這一步還能夠跳過

最後,喜歡的,請給本文點個贊,若是能夠,還請給個star,創做不易,感激涕零。🙏🙏🙏

協程正在路上,須要多一點的時間,請耐心等待。。

相關文章
相關標籤/搜索