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,创做不易,感激涕零。🙏🙏🙏

协程正在路上,须要多一点的时间,请耐心等待。。

相关文章
相关标签/搜索