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

RxCache源码心得(2)

王宏扬
2023-12-01

RxCache创建缓存接口的核心代码是下面这一段

mCacheProviders = new RxCache.Builder()
                .persistence(cacheDirectory,new GsonSpeaker())
                .using(CacheProviders.class);

创建RxCache的内部类Builder。这里可以选择通过创建的Builder的setMaxMBPersistenceCache设置缓存的容量,useExpiredDataIfLoaderNotAvailable设置是否使用过期缓存。调用persistence设置磁盘缓存的目录以及序列化的工具,在该方法的结尾会有这么一段代码,传入了之前创建的Builder作为参数来构造RxCache。

return new RxCache(this);

然后是调用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);
  }

这里使用到了动态代理。Proxy的newProxyInstance传入了所需代理的类的加载器,该类实现接口的数组,以及invocationHandler的实现类(ProxyProviders实现了InvocationHandler,重载了invoke方法),返回的代理对象可以同原对象一样调用自身方法,且invoke方法中可以对方法调用作额外的处理,相当于方法的拦截。

然后看一下ProxyProvider,首先是构造方法,用到了Dagger依赖注入,

public ProxyProviders(RxCache.Builder builder, Class<?> providersClass) {
      processorProviders = DaggerRxCacheComponent.builder()
        .rxCacheModule(new RxCacheModule(builder.getCacheDirectory(),
            builder.useExpiredDataIfLoaderNotAvailable(),
            builder.getMaxMBPersistenceCache(), getEncryptKey(providersClass),
            getMigrations(providersClass), builder.getJolyglot()))
        .build().providers();

    proxyTranslator = new ProxyTranslator();
 }

简单来说就是将processorProviders的创建独立出来。RxCacheModule构造函数中传入了许多需要注入的对象,比如从RxCache.builder中获取的参数(上文有提到),还有根据缓存接口的方法注解获取的参数。首先对象DaggerRxCacheComponent(需要编译),其中有这两个方法(因为对Dagger还不是太熟悉,才去看了一下内部实现方法。。):

@Override
  public ProcessorProviders providers() {
    return RxCacheModule_ProvideProcessorProvidersFactory.proxyProvideProcessorProviders(
        rxCacheModule, getProcessorProvidersBehaviour());
  }

...
private ProcessorProvidersBehaviour getProcessorProvidersBehaviour() {
    return new ProcessorProvidersBehaviour(
        twoLayersCacheProvider.get(),
        useExpiredDataIfLoaderNotAvailableProvider.get(),
        evictExpiredRecordsPersistenceProvider.get(),
        getGetDeepCopy(),
        getDoMigrations());
  }

可以看出processorProvider的创建是在这里完成的,再看一下RxCacheModule,结合一下上文所说的。其实ProcessorProvider的构造函数中的参数,只要构成他们的基本元素(就是有@Providers注解的方法提供的)都有,就可以完成构造。顺便提一句,RxCache这个项目里基本上都是用Dagger在进行类的创建管理。

  public RxCacheModule(File cacheDirectory, Boolean useExpiredDataIfLoaderNotAvailable,
      Integer maxMgPersistenceCache,
      String encryptKey, List<MigrationCache> migrations, JolyglotGenerics jolyglot) {
    this.cacheDirectory = cacheDirectory;
    this.useExpiredDataIfLoaderNotAvailable = useExpiredDataIfLoaderNotAvailable;
    this.maxMgPersistenceCache = maxMgPersistenceCache;
    this.encryptKey = encryptKey;
    this.migrations = migrations;
    this.jolyglot = jolyglot;
  }

  @Singleton @Provides File provideCacheDirectory() {
    return cacheDirectory;
  }

  @Singleton @Provides Persistence providePersistence(io.rx_cache2.internal.Disk disk) {
    return disk;
  }

  @Singleton @Provides Boolean useExpiredDataIfLoaderNotAvailable() {
    return useExpiredDataIfLoaderNotAvailable;
  }

  @Singleton @Provides io.rx_cache2.internal.Memory provideMemory() {
    return new ReferenceMapMemory();
  }

  @Singleton @Provides Integer maxMbPersistenceCache() {
    return maxMgPersistenceCache != null ? maxMgPersistenceCache : 100;
  }

  @Singleton @Provides Encryptor provideEncryptor() {
    return new BuiltInEncryptor();
  }

  @Singleton @Provides String provideEncryptKey() {
    return encryptKey != null ? encryptKey : "";
  }

  @Singleton @Provides List<MigrationCache> provideMigrations() {
    return migrations != null ? migrations : new ArrayList<MigrationCache>();
  }

  @Singleton @Provides JolyglotGenerics provideJolyglot() {
    return jolyglot;
  }

  @Provides io.rx_cache2.internal.ProcessorProviders provideProcessorProviders(
      io.rx_cache2.internal.ProcessorProvidersBehaviour processorProvidersBehaviour) {
    return processorProvidersBehaviour;
  }
}

上面讲到ProxyProvider这个实现了InvocationHandler的类可以重载invoke来对缓存接口方法进行拦截处理,并且在ProxyProvider自己的构造函数中用依赖注入生成了processorProviders对象。接下来看一下invoke方法:

 @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();
  }

首先关注一下proxyTranslator.processMethod(method,args),这里传入的就是缓存接口的方法和方法带的参数。再看一下具体实现:

private final Map<Method, ConfigProvider> configProviderMethodCache;

...


ConfigProvider processMethod(Method method, Object[] objectsMethod) {
    ConfigProvider prev = loadConfigProviderMethod(method);

    ConfigProvider configProvider = new ConfigProvider(prev.getProviderKey(),
        null, prev.getLifeTimeMillis(), prev.requiredDetailedResponse(), prev.isExpirable(),
        prev.isEncrypted(), getDynamicKey(method, objectsMethod),
        getDynamicKeyGroup(method, objectsMethod),
        getLoaderObservable(method, objectsMethod),
        evictProvider(method, objectsMethod));

    return configProvider;
  }

...

private ConfigProvider loadConfigProviderMethod(Method method) {
    ConfigProvider result;
    synchronized (configProviderMethodCache) {
      result = configProviderMethodCache.get(method);
      if (result == null) {
        result = new ConfigProvider(getProviderKey(method),
            null, getLifeTimeCache(method),
            requiredDetailResponse(method), getExpirable(method), isEncrypted(method),
            null, null, null, null);
        configProviderMethodCache.put(method, result);
      }
    }
    return result;
  }

configProviderMethodCache是以method为键,ConfigProvider为值的HashMap,将接口方法和其对应的配置(方法注解,方法参数)保存起来。每次调用先在HashMap中找对应的ConfigProvider,如果找不到则新建一个,其中的构造函数最后四个参数都是null,然后将其存入HashMap,表明HashMap中存入的是不包含接口缓存方法参数的信息的。而这些信息在每次调用proxyTranslator.processMethod时,会进行获取。具体如何获取接口缓存的注解信息,参数信息不详细说了。可以看一下这篇文章:https://www.jianshu.com/p/5d73909c7068

然后就是processorProviders.process,传入的是缓存接口方法的配置信息。所以关注到ProcessorProvidersBehaviour,在说它的process方法前,先看一下这个:

 @Inject public ProcessorProvidersBehaviour(...) {
    ...
    this.oProcesses = startProcesses(doMigrations, evictExpiredRecordsPersistence);
  }

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

 

ProcessorProviderBehaviour在创建过程先进行了Migration(数据迁移),具体的可以看一下@SchedueMigration和@Migration注解的使用方法。聚焦到doMigrations.react(),doMigration也是依赖注入的,构造的参数是磁盘缓存(persistence),@Migration注解的集合(List<Migration>),解密的密钥(encryptKey)。

 @Inject public DoMigrations(Persistence persistence, List<MigrationCache> migrations,
      String encryptKey) {
    this.getClassesToEvictFromMigrations = new GetClassesToEvictFromMigrations();
    this.getCacheVersion = new io.rx_cache2.internal.migration.GetCacheVersion(persistence);
    this.getPendingMigrations = new io.rx_cache2.internal.migration.GetPendingMigrations();
    this.migrations = migrations;
    this.upgradeCacheVersion = new io.rx_cache2.internal.migration.UpgradeCacheVersion(persistence);
    this.deleteRecordMatchingClassName = new io.rx_cache2.internal.migration.DeleteRecordMatchingClassName(persistence, encryptKey);
  }

上面创建的几个类,接下来会接着分析react方法一一讲清楚,

public Observable<Integer> react() {
    return getCacheVersion.react()
        .flatMap(new Function<Integer, ObservableSource<List<MigrationCache>>>() {
          @Override public ObservableSource<List<MigrationCache>> apply(Integer currentCacheVersion)
              throws Exception {
            return getPendingMigrations.with(currentCacheVersion, migrations).react();
          }
        })
        .flatMap(new Function<List<MigrationCache>, ObservableSource<List<Class>>>() {
          @Override public ObservableSource<List<Class>> apply(List<MigrationCache> migrationCaches)
              throws Exception {
            return getClassesToEvictFromMigrations.with(migrationCaches).react();
          }
        }).flatMap(new Function<List<Class>, ObservableSource<Integer>>() {
          @Override public ObservableSource<Integer> apply(List<Class> classes) throws Exception {
            return deleteRecordMatchingClassName.with(classes).react();
          }
        })
        .flatMap(new Function<Integer, ObservableSource<Integer>>() {
          @Override public ObservableSource<Integer> apply(Integer ignore) throws Exception {
            return upgradeCacheVersion.with(migrations).react();
          }
        });
  }

这些都是调用RxJava中的flatMap操作符来表示整个迁移过程。首先是getCacheVersion.react(),

  Observable<Integer> react() {
    Integer currentVersion = persistence.retrieve(KEY_CACHE_VERSION, Integer.class, false, null);
    currentVersion = currentVersion == null ? 0 : currentVersion;
    return Observable.just(currentVersion);
  }

意为获取当前的版本,这里的获取是通过persistence.retrieve,也就是读取缓存目录下KEY_CACHE_VERSION名字文件,取得一个版本号版本号,然后Observable.just发射版本号。

 public Observable<List<io.rx_cache2.MigrationCache>> react() {
    if (migrations == null || migrations.isEmpty()) {
      return Observable.just((List<io.rx_cache2.MigrationCache>) new ArrayList<io.rx_cache2.MigrationCache>());
    }

    Collections.sort(migrations, new Comparator<io.rx_cache2.MigrationCache>() {
      @Override public int compare(
          io.rx_cache2.MigrationCache migration1, io.rx_cache2.MigrationCache migration2) {
        return migration1.version() - migration2.version();
      }
    });

    List<io.rx_cache2.MigrationCache> pendingMigrations = new ArrayList<>();

    for (io.rx_cache2.MigrationCache migration : migrations) {
      if (cacheVersion < migration.version()) {
        pendingMigrations.add(migration);
      }
    }

    return Observable.just(pendingMigrations);
  }

getPendingMigrations.react()方法主要比较Migration集合的版本号和当前版本号,筛选出比当前版本新的Migration。其中对这个集合按照版本号进行排序,加快筛选速度。

Observable<List<Class>> react() {
    List<Class> classesToEvict = new ArrayList<>();

    for (io.rx_cache2.MigrationCache migration : migrations) {
      for (Class candidate : migration.evictClasses()) {
        if (!isAlreadyAdded(classesToEvict, candidate)) classesToEvict.add(candidate);
      }
    }

    return Observable.just(classesToEvict);
  }

getClassesToEvictFromMigration.react(),作用是从Migrations中获取要清理的类型。将取得的类型去重后存入List当中。

public Observable<Integer> react() {
    if (classes.isEmpty()) return Observable.just(1);

    List<String> allKeys = persistence.allKeys();

    for (String key : allKeys) {
      io.rx_cache2.internal.Record record = persistence.retrieveRecord(key, false, encryptKey);

      if (record == null) {
        record = persistence.retrieveRecord(key, true, encryptKey);
      }

      if (evictRecord(record)) {
        persistence.evict(key);
      }
    }

    return Observable.just(1);
  }

deleteRecordMatchingClassName.react(),遍历磁盘缓存,匹配到类型相同的缓存进行清理。

Observable<Integer> react() {
    if (migrations == null || migrations.isEmpty()) return Observable.just(1);

    io.rx_cache2.MigrationCache migration = migrations.get(migrations.size() - 1);
    persistence.save(KEY_CACHE_VERSION, migration.version(), false, null);

    return Observable.just(1);
  }

 upgradeCacheVersion.react(),更新版本号,取的是集合中最后一个元素的版本号,因为之前排过序。

数据迁移主要的原因应该是同样的缓存接口,接受的类型有所变化,所以导致旧缓存无法使用,这时候就要根据旧缓存的旧类型进行缓存清理。


总结一下,这一章主要讲了部分RxCache的调用过程,包括,RxCache的builder用来设置参数,ProxyProviders如何作为接口缓存的代理对象,ProxyTranslator将接口缓存方法的参数和注解转换成ConfigProvider,processorProvider的依赖注入,还有数据迁移的具体实现。

 类似资料: