当前位置: 首页 > 工具软件 > RxCache > 使用案例 >

RxCache 分析与修改

沈自珍
2023-12-01

确认需求

  1. 打开App,请求数据时先检查缓存数据是否需要迁移,如果有缓存数据升级,那么就删除原有的缓存数据。
  2. 如果有缓存数据,判断数据是否过期,如果过期则网络请求,如果没有过期,则返回缓存数据。
  3. 如果网络请求成功,返回数据,同时更新缓存信息
  4. 如果网络请求失败,返回缓存信息。
  5. 如果网络失败,缓存为空,则返回空数据或者抛出异常信息

改动点

  1. 每次读取数据之后都会处理过期数据,不满足要求4
  2. 先读缓存,再网络加载
/core/internal/cache/RetrieveRecord.java
  <T> Record<T> retrieveRecord(String providerKey, String dynamicKey, String dynamicKeyGroup,
      boolean useExpiredDataIfLoaderNotAvailable, Long lifeTime, boolean isEncrypted) {
    String composedKey = composeKey(providerKey, dynamicKey, dynamicKeyGroup);

    Record<T> record = memory.getIfPresent(composedKey);

    if (record != null) {
      record.setSource(Source.MEMORY);
    } else {
      try {
        record = persistence.retrieveRecord(composedKey, isEncrypted, encryptKey);
        record.setSource(Source.PERSISTENCE);
        memory.put(composedKey, record);
      } catch (Exception ignore) {
        return null;
      }
    }

    record.setLifeTime(lifeTime);

//    删除缓存过期的逻辑
//    if (hasRecordExpired.hasRecordExpired(record)) {
//      if (!dynamicKeyGroup.isEmpty()) {
//        evictRecord.evictRecordMatchingDynamicKeyGroup(providerKey, dynamicKey,
//            dynamicKeyGroup);
//      } else if (!dynamicKey.isEmpty()) {
//        evictRecord.evictRecordsMatchingDynamicKey(providerKey, dynamicKey);
//      } else {
//        evictRecord.evictRecordsMatchingProviderKey(providerKey);
//      }
//
//      return useExpiredDataIfLoaderNotAvailable ? record : null;
//    }
    return record;
  }
  1. 在请求前,会判断是否过期,同时处理所有过期数据
/core/internal/ProcessorProvidersBehaviour

    private Observable<Integer> startProcesses(
      io.rx_cache2.internal.migration.DoMigrations doMigrations,
      final io.rx_cache2.internal.cache.EvictExpiredRecordsPersistence evictExpiredRecordsPersistence) {
      // 处理迁移数据,我们只需要处理迁移就好
    Observable<Integer> oProcesses = doMigrations.react().flatMap(new Function<Integer, ObservableSource<Integer>>() {
          @Override public ObservableSource<Integer> apply(Integer ignore) throws Exception {
            // 删除所有过期数据,注释这段逻辑
            return evictExpiredRecordsPersistence.startEvictingExpiredRecords();
          }
        }).subscribeOn((Schedulers.io())).observeOn(Schedulers.io()).share();

    oProcesses.subscribe(new Consumer<Integer>() {
      @Override public void accept(Integer ignore) throws Exception {
        hasProcessesEnded = true;
      }
    });

    return oProcesses;
  }

  1. 在缓存过期之后,网络请求失败的同时,依然会处理过期数据
/core/internal/ProcessorProvidersBehaviour
    private Observable<Reply> getDataFromLoader(final io.rx_cache2.ConfigProvider configProvider,
      final Record record)
      ...
    .onErrorReturn(new Function<Object, Object>() {
      @Override public Object apply(Object o) throws Exception {
        如果网络错误,会判断这个 provider 的数据是否过期,如果过期,就删除掉
        // clearKeyIfNeeded(configProvider);

        boolean useExpiredData = configProvider.useExpiredDataIfNotLoaderAvailable() != null ?
            configProvider.useExpiredDataIfNotLoaderAvailable()
            : useExpiredDataIfLoaderNotAvailable;

        if (useExpiredData && record != null) {
          return new Reply(record.getData(), record.getSource(), configProvider.isEncrypted());
        }

        throw new io.rx_cache2.RxCacheException(io.rx_cache2.internal.Locale.NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER
            + " "
            + configProvider.getProviderKey(), (Throwable) o);
      }
    });

  1. 数据迁移过程的bug, 如果缓存为空
/internal/migration/DeleteRecordMatchingClassName.java

private boolean evictRecord(io.rx_cache2.internal.Record record) {
    没有判断 record 为空的情况。
    //if(record == null) return false;
    String candidate = record.getDataClassName();

    for (Class aClass : classes) {
      String className = aClass.getName();
      if (className.equals(candidate)) {
        return true;
      }
    }

    return false;
  }
  1. 一些小改动,小判断
//VisibleForTesting
  <T> Observable<T> getData(final io.rx_cache2.ConfigProvider configProvider) {
    Record<Object> record = twoLayersCache.retrieve(configProvider.getProviderKey(), configProvider.getDynamicKey(),
        configProvider.getDynamicKeyGroup(), useExpiredDataIfLoaderNotAvailable,
        configProvider.getLifeTimeMillis(), configProvider.isEncrypted());

    Observable<Reply> replyObservable;
    Observable<Reply> remoteObservable = getDataFromLoader(configProvider, record);
    if(record == null) {
      // only load from network
      replyObservable = remoteObservable;
    } else {
      Observable<Reply> localObservable = Observable.just(new Reply(record.getData(), record.getSource(), configProvider.isEncrypted()));
      if(!evictExpiredRecordsPersistence.isExpired(record) && !configProvider.evictProvider().evict()) {
          replyObservable = localObservable;
        } else {
          replyObservable = Observable.concat(localObservable, remoteObservable);
        }
    }
    return (Observable<T>) replyObservable.map(new Function<Reply, Object>() {
      @Override public Object apply(Reply reply) throws Exception {
        return ProcessorProvidersBehaviour.this.getReturnType(configProvider, reply);
      }
    });
  }

最后更换依赖

1. Fork代码,修改代码,创建tag,发布到jitpack.io, 客户端更换依赖就好了
2. implementation 'com.github.weixuefeng.RxCache-1:runtime:1.8.7-3x'

感谢

  1. 你不知道的Retrofit缓存库RxCache JessYan 的介绍。
  2. RxCache GitHub 链接
  3. RxCache源码分析

原文链接

  1. 原文链接
 类似资料: