RxCache源碼分析

RxCache

是使用註解爲Retrofit加入二級緩存(內存,磁盤)的緩存庫。 開頭膜拜大神
項目地址 : RxCache

RxCache使用方法

定義接口

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

十分簡潔,使用了註解@LifeCache聲明瞭緩存的超時時間(duration長度,timeUnit時間單位),接口定義了之後,如何實例化這個接口,看下面。

創建CacheProviders對象

providers = new RxCache.Builder()
                .persistence(cacheDir, new GsonSpeaker())
                .using(Providers.class);

之後的使用和Retrofit無異

實例化的過程是比較常見的Builder模式,和Retrofit的API的實例化的方式很像,調用using()就創建了接口的實例,和Retrofitcreate()方法也十分相似,當然內部實現也很相似(都是使用了動態代理)。

RxCache源碼分析

using() 方法創建CacheProviders接口實例的過程

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

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

創建接口的實例使用的是動態代理技術

簡而言之,就是動態生成接口的實現類(當然生成實現類有緩存機制),並創建其實例(稱之爲代理),代理把對接口的調用轉發給 InvocationHandler實例,而在 InvocationHandler的實現中,除了執行真正的邏輯(例如再次轉發給真正的實現類對象),我們還可以進行一些有用的操作,例如統計執行時間、進行初始化和清理、對接口調用進行檢查等。

爲什麼要用動態代理? 因爲對接口的所有方法的調用都會集中轉發到 InvocationHandler#invoke函數中,我們可以集中進行處理,更方便了。你可能會想,我也可以手寫這樣的代理類,把所有接口的調用都轉發到InvocationHandler#invoke呀,當然可以,但是可靠地自動生成豈不更方便?

RxCache 1.5.2版本中,上述ProxyProviders對象的創建方法不是簡單的new,而是使用了依賴注入框架dagger,dagger在後面的還有使用,這裏只是提一下。

在這裏ProxyProviders實現了InvocationHandler,所以代理把對接口的調用轉發給了ProxyProviders#invoke,所以我們就看下invoke()方法。

@Override public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    return Observable.defer(new Func0<Observable<Object>>() {
      @Override public Observable<Object> call() {
        return processorProviders.process(proxyTranslator.processMethod(method, args));
      }
    });

method包含了調用方法的基本信息(註解,返回值類型等),args是方法的參數列表。

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

ConfigProvider是一個包裝類,裏面包裝了該方法本身的屬性(名稱,相關注解,超時時間之類的)以及方法的調用參數,。

ProxyTranslator#processMethod這個方法先通過ProxyTranslator#loadConfigProviderMethodload出了一個ConfigProvider對象,這個load方法是先從map中讀緩存,沒有緩存再新創建實例(下面的configProviderMethodCache是一個HashMap),這裏也和Retrofit中的請求方法的緩存方式一樣。

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

ProxyTranslator中提供了很多的get()方法,這些get方法可以分爲兩種,一種是只需要傳入一個參數method,另一種需要傳入兩個參數methodobjectsMethod。這裏以ProxyTranslator#getLifeTimeCache(一個參數),ProxyTranslator#getDynamicKey(兩個參數)爲例。

private Long getLifeTimeCache(Method method) {
    LifeCache lifeCache = method.getAnnotation(LifeCache.class);
    if (lifeCache == null) return null;
    return lifeCache.timeUnit().toMillis(lifeCache.duration());
  }

一個參數的很簡單,將註解中的超時時間讀取出來並換算爲毫秒,其他的get()方法也相同,都是爲了從註解中取出信息

private String getDynamicKey(Method method, Object[] objectsMethod) {
    DynamicKey dynamicKey = getObjectFromMethodParam(method, DynamicKey.class, objectsMethod);
    if (dynamicKey != null) return dynamicKey.getDynamicKey().toString();

    DynamicKeyGroup dynamicKeyGroup =
        getObjectFromMethodParam(method, DynamicKeyGroup.class, objectsMethod);
    if (dynamicKeyGroup != null) return dynamicKeyGroup.getDynamicKey().toString();

    return "";
  }

兩個參數的也不復雜,比較重要的是ProxyTranslator#getObjectFromMethodParam,這個方法傳入三個參數method,Class對象(從參數數組中取出參數的類型),objectsMethod(參數數組)

 private <T> T getObjectFromMethodParam(Method method, Class<T> expectedClass,
      Object[] objectsMethod) {
    int countSameObjectsType = 0;
    T expectedObject = null;

    for (Object objectParam : objectsMethod) {
      if (expectedClass.isAssignableFrom(objectParam.getClass())) {
        expectedObject = (T) objectParam;
        countSameObjectsType++;
      }
    }

    if (countSameObjectsType > 1) {
      String errorMessage =
          method.getName() + Locale.JUST_ONE_INSTANCE + expectedObject.getClass().getSimpleName();
      throw new IllegalArgumentException(errorMessage);
    }

    return expectedObject;
  }

通過代碼可以知道,傳入Class對象是爲了通過遍歷取出和你期待的參數類型相同的參數(注意這個isAssignableFrom方法,是用來判斷一個類和另一個類是否相同或是另一個類的超類或接口,注意countSameObjectsType這個整形,就是爲了根據作者的本意的約定,防止有繼承相同父類或者實現相同接口的參數類型出現)並返回,至此,兩個參數的get()方法意圖也很明顯根據類型取出從外部調用時的參數對象

從上面分析看來ProxyTranslator#processMethod的過程就是對一次動態代理的invoke()過程的再封裝,返回的是方法調用的信息的集合(包括方法的參數,註解包含的信息,返回值等),接下來又回到了動態代理中真正的InvocationHandler實現類ProxyProviders中,繼續看真正的invoke()方法

@Override public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    return Observable.defer(new Func0<Observable<Object>>() {
      @Override public Observable<Object> call() {
        return processorProviders.process(proxyTranslator.processMethod(method, args));
      }
    });

又粘貼了下上面的代碼哈,這次是ProcessorProviders#process,這個ProcessorProviders點進去看是一個接口,那麼真正的實現類呢?

processorProviders = DaggerRxCacheComponent.builder()
        .rxCacheModule(new RxCacheModule(builder.getCacheDirectory(),
            builder.useExpiredDataIfLoaderNotAvailable(),
            builder.getMaxMBPersistenceCache(), getEncryptKey(providersClass),
            getMigrations(providersClass), builder.getJolyglot()))
        .build().providers();

實例化ProcessorProviders的過程在ProxyProviders的構造函數裏。使用了依賴注入框架dagger,本文對dagger的用法不做介紹。點進RxCacheModule#provideProcessorProviders裏面返回值類型是ProcessorProvidersBehaviour,它正是ProcessorProviders的實現類。我們主要關心ProcessorProviders#process這個方法。

@Override
  public <T> Observable<T> process(final ConfigProvider configProvider) {
    return (Observable<T>) Observable.defer(new Func0<Observable<Object>>() {
      @Override public Observable<Object> call() {
        if (hasProcessesEnded) {
          return getData(configProvider);
        }

        return oProcesses.flatMap(new Func1<Void, Observable<?>>() {
          @Override public Observable<?> call(Void aVoid) {
            return getData(configProvider);
          }
        });
      }
    });
  }

而這個方法主要定位到getData()再由這個方法繼續定位下去是TwoLayersCache#retrieve,方法返回值類型爲Record而且被Observable#just發射

<T> Observable<T> getData(final ConfigProvider configProvider) {
    return (Observable<T>) Observable.just(
        twoLayersCache.retrieve(configProvider.getProviderKey(), configProvider.getDynamicKey(),
            configProvider.getDynamicKeyGroup(), useExpiredDataIfLoaderNotAvailable,
            configProvider.getLifeTimeMillis(), configProvider.isEncrypted()))
        .map(new Func1<Record, Observable<Reply>>() {
          @Override public Observable<Reply> call(final Record record) {
            if (record != null && !configProvider.evictProvider().evict()) {
              return Observable.just(
                  new Reply(record.getData(), record.getSource(), configProvider.isEncrypted()));
            }

            return getDataFromLoader(configProvider, record);
          }
        }).flatMap(new Func1<Observable<Reply>, Observable<Object>>() {
          @Override
          public Observable<Object> call(Observable<Reply> responseObservable) {
            return responseObservable.map(new Func1<Reply, Object>() {
              @Override
              public Object call(Reply reply) {
                return getReturnType(configProvider, reply);
              }
            });
          }
        });

TwoLayersCache#retrieve接收configProvider傳過來的各種參數,直接將參數return到方法RetrieveRecord#retrieveRecord
這個方法也是讀取緩存的真正邏輯所在,讀取的結果以Record類型返回,RxCache還支持類似於Spring@CacheEvict按key清除緩存功能,看代碼和註釋。

<T> Record<T> retrieveRecord(String providerKey, String dynamicKey, String dynamicKeyGroup,
      boolean useExpiredDataIfLoaderNotAvailable, Long lifeTime, boolean isEncrypted) {
    String composedKey = composeKey(providerKey, dynamicKey, dynamicKeyGroup);
    //根據key從內存中讀數據
    Record<T> record = memory.getIfPresent(composedKey);

    if (record != null) {
      //內存不爲空,將source標記爲內存
      record.setSource(Source.MEMORY);
    } else {
      try {
        //爲空就從磁盤讀取,將source標記爲磁盤
        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)) {
      //下面就是根據EvictDynamicKey/EvictDynamicKeyGroup做緩存清除的工作,根據作者Github介紹,RxCache支持類似於Spring的@CacheEvict功能
      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;
  }

上面說到返回的RecordObservable#just發射又由Observable#map轉換爲Observable<Reply>,之後有一個判斷,就是當Record != null並且沒有被標記需要清空全部緩存的時候才繼續講Record轉換爲Reply併發射。不滿足條件的話就執行ProcessorProvidersBehaviour#getDataFromLoader,就是將使用的時候傳入的外部請求(也是Observable,通常是Retrofit的接口請求方法)進行請求,將數據寫入TwoLayersCache並將數據的Source標記爲CLOUD(雲端),以Observable<Reply>形式返回。

之後又做了一次Observable#flatMapObservable#map,在map中做了一次判斷if(configProvider.requiredDetailedResponse()),就返回帶有詳細信息的數據(Reply類型)return new Reply<>(data, reply.getSource(), configProvider.isEncrypted());否則就返回data的深拷貝(Object類型),當然最後整個方法ProcessorProvidersBehaviour#getData的返回還是一個Observable供外部調用。

最後又回到了實現了InvocationHandler接口的ProxyProviders#invoke,還是動態代理哈,將接口的調用轉發給ProxyProviders#invoke,真正的方法在這裏面實現就好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章