RxCache原理分析

本文详细分析了RxCache的缓存方案,从初始化、动态代理到核心的缓存策略,包括内存和磁盘存储。通过注解定义接口方法,借助动态代理实现接口的具体实现。缓存策略主要在TwoLayersCache类中,包括删除、获取和保存数据的三个关键操作。文章还探讨了数据的获取流程,涉及网络加载和Retrofit的结合使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RxCache配合retrofit使用的缓存方案

首先看一下rxcache的官方demo

/**
 * 此为RxCache官方Demo
 */
public interface CacheProviders {

    @LifeCache(duration = 2, timeUnit = TimeUnit.MINUTES)
    Observable<Reply<List<Repo>>> getRepos(Observable<List<Repo>> oRepos, DynamicKey userName, EvictDynamicKey evictDynamicKey);

    @LifeCache(duration = 2, timeUnit = TimeUnit.MINUTES)
    Observable<Reply<List<User>>> getUsers(Observable<List<User>> oUsers, DynamicKey idLastUserQueried, EvictProvider evictProvider);

    Observable<Reply<User>> getCurrentUser(Observable<User> oUser, EvictProvider evictProvider);
}

接下来我们看一下RxCache的初始化

 CacheProviders cacheProviders = new RxCache.Builder()
                .persistence(cacheDir, new GsonSpeaker())
                .using(CacheProviders.class);

关于rxache的一些基本使用请参考下文
https://www.jianshu.com/p/b58ef6b0624b

进入源码之前我们首先思考一下这么几个问题,
1,通过上面的例子我们可以看出rxchace通过注解的方式定义了接口方法,那么它是在什么地方具体实现的
2,既然是缓存,它都有哪些缓存策略

接下来我们进入RxCache类的using方法看下这个方法都干了些什么
下面看下源码

  public <T> T using(final Class<T> classProviders) {
    proxyProviders = new ProxyProviders(builder, classProviders);

    return (T) Proxy.newProxyInstance(
        classProviders.getClassLoader(),
        new Class<?>[] {classProviders},
        proxyProviders);
  }

从这里我们可以看出,这里是通过动态代理的方式,返回了一个执行对象,这个对象就是传入的接口的具体实现类。关于不了解动态代码机制的自行研究学习

接下来,我们进入ProxyProviders,ProxyProviders继承InvocationHandler,这个类是动态代理的关键类,主要方法是invoke,进入ivoke方法

 @Override public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    return Observable.defer(new Callable<ObservableSource<?>>() {
      @Override public ObservableSource<?> call() throws Exception {
        Observable observable = processorProviders.process(proxyTranslator.processMethod(method, args));
        Class<?> methodType = method.getReturnType();

        if (methodType == Observable.class) return Observable.just(observable);

        if (methodType == Single.class) return Observable.just(Single.fromObservable(observable));

        if (methodType == Maybe.class) {
          return Observable.just(Maybe.fromSingle(Single.fromObservable(observable)));
        }

        if (method.getReturnType() == io.reactivex.Flowable.class) {
          return Observable.just(observable.toFlowable(BackpressureStrategy.MISSING));
        }

        String errorMessage = method.getName() + io.rx_cache2.internal.Locale.INVALID_RETURN_TYPE;
        throw new RuntimeException(errorMessage);
      }
    }).blockingFirst();
  }

这里关键代码是
Observable observable = processorProviders.process(proxyTranslator.processMethod(method, args));
在这里processorProviders是一个接口,已经在ProxyProviders的构造器里面初始化过。这里的初始化方式是通过Dagger的方式,具体Dagger的使用方式自行研究。最终processorProviders实现类是ProcessorProvidersBehaviour。接下来我们进入ProcessorProvidersBehaviour类的process方法

  @Override
  public <T> Observable<T> process(final io.rx_cache2.ConfigProvider configProvider) {
    return Observable.defer(new Callable<ObservableSource<? extends T>>() {
      @Override public ObservableSource<? extends T> call() throws Exception {
        if (hasProcessesEnded) {
          return getData(configProvider);
        }

        return oProcesses.flatMap(new Function<Integer, ObservableSource<? extends T>>() {
          @Override public ObservableSource<? extends T> apply(Integer ignore) throws Exception {
            return getData(configProvider);
          }
        });
      }
    });
  }

在这里,有一些版本迁移的操作,先不管它,看关键代码getDaga(),这个方法是获取数据的,下面进入源码。

//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;

    if (record != null && !configProvider.evictProvider().evict()) {
      replyObservable = Observable.just(new Reply(record.getData(), record.getSource(), configProvider.isEncrypted()));
    } else {
      replyObservable = getDataFromLoader(configProvider, record);
    }

    return (Observable<T>) replyObservable.map(new Function<Reply, Object>() {
      @Override public Object apply(Reply reply) throws Exception {
        return ProcessorProvidersBehaviour.this.getReturnType(configProvider, reply);
      }
    });
  }

这里的关键代码是

if (record != null && !configProvider.evictProvider().evict()) {
  replyObservable = Observable.just(new Reply(record.getData(), record.getSource(), configProvider.isEncrypted()));
} else {
  replyObservable = getDataFromLoader(configProvider, record);
}

这两句代码的意思是有缓存就从缓存取数据,否在走网络通道。else分支就是走的网络通道

接下来我们继续看取缓存的代码。这里的关键点在record,我们这里看一下record初始化的地方,
Record record = twoLayersCache.retrieve(configProvider.getProviderKey(), configProvider.getDynamicKey(),
从这里我们可以看出record是通过TwoLayersCache这个类初始化的,接下来我们进入这个类,这个类很关键,这里就是它的缓存主要策略实现的地方


@Singleton
public final class TwoLayersCache {
  private final EvictRecord evictRecord;
  private final io.rx_cache2.internal.cache.RetrieveRecord retrieveRecord;
  private final SaveRecord saveRecord;

  @Inject public TwoLayersCache(EvictRecord evictRecord, io.rx_cache2.internal.cache.RetrieveRecord retrieveRecord,
      SaveRecord saveRecord) {
    this.evictRecord = evictRecord;
    this.retrieveRecord = retrieveRecord;
    this.saveRecord = saveRecord;
  }

  public <T> Record<T> retrieve(String providerKey, String dynamicKey, String dynamicKeyGroup,
      boolean useExpiredDataIfLoaderNotAvailable, Long lifeTime, boolean isEncrypted) {
    return retrieveRecord.retrieveRecord(providerKey, dynamicKey, dynamicKeyGroup,
        useExpiredDataIfLoaderNotAvailable, lifeTime, isEncrypted);
  }

  public void save(String providerKey, String dynamicKey, String dynamicKeyGroup, Object data,
      Long lifeTime, boolean isExpirable, boolean isEncrypted) {
    saveRecord.save(providerKey, dynamicKey, dynamicKeyGroup, data, lifeTime, isExpirable,
        isEncrypted);
  }

  public void evictProviderKey(final String providerKey) {
    evictRecord.evictRecordsMatchingProviderKey(providerKey);
  }

  public void evictDynamicKey(String providerKey, String dynamicKey) {
    evictRecord.evictRecordsMatchingDynamicKey(providerKey, dynamicKey);
  }

  public void evictDynamicKeyGroup(String key, String dynamicKey, String dynamicKeyGroup) {
    evictRecord.evictRecordMatchingDynamicKeyGroup(key, dynamicKey, dynamicKeyGroup);
  }

  public void evictAll() {
    evictRecord.evictAll();
  }

  //Exists for testing purposes
  public void mockMemoryDestroyed() {
    evictRecord.mockMemoryDestroyed();
  }
}

这个类很关键,代码量也不多,所有这里我把它都贴了进来,这个类有三个关键的成员变量
private final EvictRecord evictRecord;
private final io.rx_cache2.internal.cache.RetrieveRecord retrieveRecord;
private final SaveRecord saveRecord;
顾名思义,
evictRecord 驱逐数据,就是删除本地对应缓存
retrieveRecord 获取数据,从本地获取缓存
saveRecord 保存数据,将新数据存入缓存

这个三个类都继承自Action,接下来进入最后一步,看下这个类的源码

 private static final String PREFIX_DYNAMIC_KEY = "$d$d$d$";
  private static final String PREFIX_DYNAMIC_KEY_GROUP = "$g$g$g$";

  protected final Memory memory;
  protected final Persistence persistence;

  public Action(Memory memory, Persistence persistence) {
    this.memory = memory;
    this.persistence = persistence;
  }

  protected String composeKey(String providerKey, String dynamicKey, String dynamicKeyGroup) {
    return providerKey
        + PREFIX_DYNAMIC_KEY
        + dynamicKey
        + PREFIX_DYNAMIC_KEY_GROUP
        + dynamicKeyGroup;
  }

  protected List<String> getKeysOnMemoryMatchingProviderKey(String providerKey) {
    List<String> keysMatchingProviderKey = new ArrayList<>();

    for (String composedKeyMemory : memory.keySet()) {
      final String keyPartProviderMemory =
          composedKeyMemory.substring(0, composedKeyMemory.lastIndexOf(PREFIX_DYNAMIC_KEY));

      if (providerKey.equals(keyPartProviderMemory)) {
        keysMatchingProviderKey.add(composedKeyMemory);
      }
    }

    return keysMatchingProviderKey;
  }

  protected List<String> getKeysOnMemoryMatchingDynamicKey(String providerKey, String dynamicKey) {
    List<String> keysMatchingDynamicKey = new ArrayList<>();

    String composedProviderKeyAndDynamicKey = providerKey + PREFIX_DYNAMIC_KEY + dynamicKey;

    for (String composedKeyMemory : memory.keySet()) {
      final String keyPartProviderAndDynamicKeyMemory =
          composedKeyMemory.substring(0, composedKeyMemory.lastIndexOf(PREFIX_DYNAMIC_KEY_GROUP));

      if (composedProviderKeyAndDynamicKey.equals(keyPartProviderAndDynamicKeyMemory)) {
        keysMatchingDynamicKey.add(composedKeyMemory);
      }
    }

    return keysMatchingDynamicKey;
  }

  protected String getKeyOnMemoryMatchingDynamicKeyGroup(String providerKey, String dynamicKey,
      String dynamicKeyGroup) {
    return composeKey(providerKey, dynamicKey, dynamicKeyGroup);
  }

这个类有两个关键的成员变量
protected final Memory memory;
protected final Persistence persistence;
顾名思义: memory 内存 这个内就是操作内存的,数据在内存中的删除,查询,保存
persistence 持久层,这个就是磁盘存储, 主要操作数据在磁盘中删除,查询,保存。

它门的关系如下uml图,画的不好看将就看哈
在这里插入图片描述

总结一下,TwoLayersCache包括了三种操作,删除,保存,获取缓存的功能。
action提供了,操作磁盘和内存的两种功能。

至此我们就跟完了整体的缓存代码

有兴趣的可以返回ProcessorProvidersBehaviour类的getData方法,跟一下网络加载的分支,和Retrofit类似,

如果有什么疑惑或者错误的地方,请不吝赐教。毕竟真理越辩越明嘛

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值