Skip to content

缓存策略

liujingxing edited this page Feb 25, 2023 · 14 revisions

1、设置全局缓存策略

通过RxHttpPlugins.setCache(File, long, CacheMode, long)静态方法,设置全局缓存策略,如下:

public void initRxHttpCache(Context context) {     
    //设置缓存目录为:Android/data/{app包名目录}/cache/RxHttpCache    
    File cacheDir = new File(context.getExternalCacheDir(), "RxHttpCache"); 
    //设置最大缓存为10M,缓存有效时长为60秒 
    RxHttpPlugins.init(OkHttpClient)
        .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

    该模式下,先请求网络,请求成功,写入缓存并返回;否则读取缓存

2、设置单个请求的缓存策略

通过Rxhttp#setCacheMode(CacheMode)为单个请求设置单独的缓存策略,如下:

RxHttp.get("/service/...")
    .setCacheValidTime(10 * 1000) //当前请求缓存有效期为10秒
    .setCacheMode(CacheMode.READ_CACHE_FAILED_REQUEST_NETWORK) //当前请求先读取缓存,失败后,再请求网络
    .toObservableString()
    .subscribe(s -> {
        //成功回调            
    }, throwable -> {
        //失败回调          
    });              

以上代码,为单个请求设置了单独的缓存策略,其中有效时长为10秒,缓存模式为先读取缓存,失败后,再请求网络。如果不设置,将使用全局的缓存策略。

注:设置单独的缓存模式/缓存有效时长前,一定要设置缓存存储目录和缓存最大Size,否则无效

3、关于CacheKey

CacheKey在RxHttp的缓存模块中,是一个很重要的角色,内部缓存的读写、删除都是通过CacheKey来操作的。对于不相同的请求,要保证CacheKey的唯一性;对于相同的请求,要保证CacheKey的静态性,这两个特性非常重要,一定要保证,否则缓存将变得毫无意义。

为啥这么说,请往下看。

3.1、内部CacheKey的生成规则

RxHttp内部会根据一定规则,为每个请求,都生成一个具有唯一性的CacheKey,规则如下:

NoBodyParam

将添加的查询参数拼接在url后面,如:CacheKey = url?key=value&key1=value1... ,Get、Head类型请求遵循这个规则

FormParam

FormParam将查询参数和body参数一起拼接Url后,如:CacheKey = url?queryKey=queryValue&key=value... , xxxForm类型请求遵循这个规则

JsonParam

JsonParam将查询参数和json字符串一起拼接Url后,如:CacheKey = url?queryKey=queryValue&json=jsonStr , xxxJson类型请求遵循这个规则

JsonArrayParam

JsonArrayParam将查询参数和json字符串一起拼接Url后,如:CacheKey = url?queryKey=queryValue&json=jsonStr , xxxJsonArray类型请求遵循这个规则

以上4个规则,可在对应的Param类中builCacheKey()方法查看

注:查询参数指的是通过addQuery系列方法添加的参数,该参数不会放到body里面;通过add系列方法添加的参数就是body参数,该参数会放到body里面

3.2、自定义CacheKey

如果以上规则不能满足你的业务需求,RxHttp还提供了两种自定义CacheKey的方式,如下:

1、通过RxHttp#setCacheKey(String)方法,为单个请求指定CacheKey

RxHttp.get("/service/...")
    .setCacheKey("自定义的CacheKey")  //自定义CacheKey
    .toObservableString()
    .subscribe(s -> {
       //成功回调
    }, throwable -> {
       //失败回调
    });

2、通过自定义Param,并重写buildCacheKey()方法,为某一类请求制定CacheKey的生成规则,如下:

@Param(methodName = "postEncryptForm")
public class PostEncryptFormParam extends FormParam {

    public PostEncryptFormParam(String url) {
        super(url, Method.POST);
    }

    @Override
    public String buildCacheKey() {
        String cacheKey = null;
        String simpleUrl = getSimpleUrl(); //拿到Url
        Headers headers = getHeaders();//拿到添加的请求头
        List<KeyValuePair> keyValuePairs = getQueryParam(); //拿到查询参数
        List<KeyValuePair> keyValuePairs = getBodyParam(); //拿到body参数
        cacheKey = "根据url/请求头/请求参数,自定义规则,生成CacheKey";
        return cacheKey;
    }
}

注:自定义CacheKey,一定要保证CacheKey的唯一性,若重复,将覆盖已存在的缓存

3.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的缓存,这就是我说的缓存错乱

3.4、静态性

何为静态性?我对它的定义是:同一个请求,发送多次,要保证CacheKey是一致的,否则,缓存将毫无意义。

试想,我们有这样一种情况,每次发送请求,都要带上当前的时间戳,如下:

RxHttp.get("/service/...")   
    .add("currentTime", System.currentTimeMillis()) 
    .setCache(CacheMode.READ_CACHE_FAILED_REQUEST_NETWORK) //当前请求先读取缓存,失败后,再请求网络
    .toObservableString()    
    .subscribe(s -> {       
      //成功回调    
    }, throwable -> {       
      //失败回调    
    });

上面的代码,每次发起请求,时间戳都不一样,就导致内部每次生成的CacheKey也不一样,从而造成每次都会写入相同的缓存而不覆盖,更严重的是,每次读取缓存都会失败,那我们如何避免这种情况呢?有,前面介绍的,自定义Cachekey就能解决这个问题,那还有没有更简单的办法呢?也有,往下看。

剔除不参与CacheKey组拼的参数

我们可以调用RxHttpPlugins.setExcludeCacheKeys(String... keys)方法,设置不参与CacheKey组拼的参数,即剔除这些参数。对于上面的问题,就可以这么做:

RxHttpPlugins.setExcludeCacheKeys("currentTime")  //可变参数,可传入多个key

此时,发送请求,当前时间戳就不会参与CacheKey的组拼,从而保证了每次发起请求,CacheKey都是一样的,这就是我说的静态性