RxCacheを変更してRetrofitにキャッシュを先にロードした後にネットワーク機能を要求する


RxJavaファミリーバケツのキャッシュフレームRxCacheはキャッシュ時間の設定のみで、先にキャッシュを入れてからネットワークを要求する機能はできません.RxCacheの基本的な使用は、
http://blog.csdn.net/windboy2014/article/details/52711188
1.ソースコードをよく見てから、ソースコードをキャッシュ機能に変更することができます.RxCacheの構築方法は、Retrofitと同様にBuilderを使用して構築されています.
providers = new RxCache.Builder()
                .persistence(cacheDir, new GsonSpeaker())
                .using(Providers.class);

USingが呼び出され、usingで使用される動的エージェントのすべてのメソッドがInvocationHandlerのinvokeメソッドを実行します.
public  T using(final Class<T> classProviders) {
    proxyProviders = new ProxyProviders(builder, classProviders);

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

対応するProxyProvidersのエージェント提供クラスはRxCache 1.5.2バージョンで使用されるdagger 2方式で作成され、ProxyProvidersはInvocationHandlerを実現し、すべての方法はinvoke方法を実行します.

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

まずProxyTranslatorのprocessMethodメソッドを見てみましょう
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を使用してパッケージ化し、パッケージクラスConfigProviderを返します.また、ProcessorProvidersのprocessメソッドを振り返ると、点開はインタフェースであり、対応する実装クラスはdagger 2によって生成されます.RxCacheModule(RxCacheの注記注入パラメータを管理するクラス)のprovideProcessorProvidersメソッドの表示
@Provides ProcessorProviders provideProcessorProviders(
      ProcessorProvidersBehaviour processorProvidersBehaviour) {
    return processorProvidersBehaviour;
  }

対応する実装クラスは、P r o s e s o r P o v idersBehaviourのプロセスメソッドです.
@Override
  public  Observable process(final ConfigProvider configProvider) {
    return (Observable) Observable.defer(new Func0>() {
      @Override public Observable call() {
        if (hasProcessesEnded) {
          return getData(configProvider);
        }

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

  //VisibleForTesting
   Observable getData(final ConfigProvider configProvider) {
    return (Observable) Observable.just(
        twoLayersCache.retrieve(configProvider.getProviderKey(), configProvider.getDynamicKey(),
            configProvider.getDynamicKeyGroup(), useExpiredDataIfLoaderNotAvailable,
            configProvider.getLifeTimeMillis(), configProvider.isEncrypted()))
        .map(new Func1>() {
          @Override public Observable call(final Record record) {
            if (record != null && !configProvider.evictProvider().evict()) {
            //   evict() false    
              return Observable.just(
                  new Reply(record.getData(), record.getSource(), configProvider.isEncrypted()));
            }
            //      
            return getDataFromLoader(configProvider, record);
          }
        }).flatMap(new Func1, Observable>() {
          @Override
          public Observable call(Observable responseObservable) {
            return responseObservable.map(new Func1() {
              @Override
              public Object call(Reply reply) {
                return getReturnType(configProvider, reply);
              }
            });
          }
        });
  }

  private Observable getDataFromLoader(final ConfigProvider configProvider,
      final Record record) {
    return configProvider.getLoaderObservable().map(new Func1() {
      @Override public Reply call(Object data) {
        boolean useExpiredData = configProvider.useExpiredDataIfNotLoaderAvailable() != null ?
            configProvider.useExpiredDataIfNotLoaderAvailable()
            : useExpiredDataIfLoaderNotAvailable;

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

        if (data == null) {
          throw new RxCacheException(Locale.NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER
              + " "
              + configProvider.getProviderKey());
        }
    //      
        twoLayersCache.save(configProvider.getProviderKey(), configProvider.getDynamicKey(),
            configProvider.getDynamicKeyGroup(), data, configProvider.getLifeTimeMillis(),
            configProvider.isExpirable(), configProvider.isEncrypted());
        return new Reply(data, Source.CLOUD, configProvider.isEncrypted());
      }
    }).onErrorReturn(new Func1() {
      @Override public Object call(Object o) {
        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 RxCacheException(Locale.NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER
            + " "
            + configProvider.getProviderKey(), (Throwable) o);
      }
    });
  }

Recordはキャッシュデータを記録していますが、キャッシュロジックは基本的にこのクラスのTwoLayersCacheのretrieveメソッドにあります.
 public  Record retrieve(String providerKey, String dynamicKey, String dynamicKeyGroup,
      boolean useExpiredDataIfLoaderNotAvailable, Long lifeTime, boolean isEncrypted) {
    return retrieveRecord.retrieveRecord(providerKey, dynamicKey, dynamicKeyGroup,
        useExpiredDataIfLoaderNotAvailable, lifeTime, isEncrypted);
  }

RetrieveRecordを呼び出すretrieveRecordメソッド
 Record retrieveRecord(String providerKey, String dynamicKey, String dynamicKeyGroup,
      boolean useExpiredDataIfLoaderNotAvailable, Long lifeTime, boolean isEncrypted) {
    String composedKey = composeKey(providerKey, dynamicKey, dynamicKeyGroup);
    //       
    Record record = memory.getIfPresent(composedKey);

    if (record != null) {
    //         
      record.setSource(Source.MEMORY);
    } else {
      try {
      //SD    ,   sd   
        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;
  }

ソース解読はこの解読に基づいて
  • ソースコードを修正し、キャッシュを先に読んでからネットワークを要求する機能を実現する.主にProcessorProvidersBehaviourのgetDataメソッド
  • を修正する.
    
      //VisibleForTesting
       Observable getData(final ConfigProvider configProvider) {
        return (Observable) Observable.just(
            twoLayersCache.retrieve(configProvider.getProviderKey(), configProvider.getDynamicKey(),
                configProvider.getDynamicKeyGroup(), useExpiredDataIfLoaderNotAvailable,
                configProvider.getLifeTimeMillis(), configProvider.isEncrypted()))
            .map(new Func1>() {
              @Override public Observable call(final Record record) {
                  //evict() true no cache ,false cache data
                if (record != null && !configProvider.evictProvider().evict()) {
                    //       
                    return getDataFromClond(configProvider, record);
                }
                //           ,      
                return getDataFromLoader(configProvider, record);
              }
            }).flatMap(new Func1, Observable>() {
              @Override
              public Observable call(Observable responseObservable) {
                return responseObservable.map(new Func1() {
                  @Override
                  public Object call(Reply reply) {
                    return getReturnType(configProvider, reply);
                  }
                });
              }
            });
      }
    
      private Observable getDataFromLoader(final ConfigProvider configProvider,
          final Record record) {
        return configProvider.getLoaderObservable().map(new Func1() {
          @Override public Reply call(Object data) {
              //has Data
            boolean useExpiredData = configProvider.useExpiredDataIfNotLoaderAvailable() != null ?
                configProvider.useExpiredDataIfNotLoaderAvailable()
                : useExpiredDataIfLoaderNotAvailable;
    
            if (data == null && useExpiredData && record != null) {
              return new Reply(record.getData(), record.getSource(), configProvider.isEncrypted());
            }
    
            clearKeyIfNeeded(configProvider);
    
            if (data == null) {
              throw new RxCacheException(Locale.NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER
                  + " "
                  + configProvider.getProviderKey());
            }
            //      
            twoLayersCache.save(configProvider.getProviderKey(), configProvider.getDynamicKey(),
                configProvider.getDynamicKeyGroup(), data, configProvider.getLifeTimeMillis(),
                configProvider.isExpirable(), configProvider.isEncrypted());
            return new Reply(data, Source.CLOUD, configProvider.isEncrypted());
          }
        }).onErrorReturn(new Func1() {
          @Override public Object call(Object o) {
            boolean useExpiredData = configProvider.useExpiredDataIfNotLoaderAvailable() != null ?
                configProvider.useExpiredDataIfNotLoaderAvailable()
                : useExpiredDataIfLoaderNotAvailable;
            if (record != null) {
            //    ,        ,      
              return new Reply(record.getData(), record.getSource(), configProvider.isEncrypted());
            }
    
            throw new RxCacheException(Locale.NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER
                + " "
                + configProvider.getProviderKey(), (Throwable) o);
          }
        });
      }
      private Observable getDataFromClond(final ConfigProvider configProvider,
          final Record record) {
        return configProvider.getLoaderObservable().map(new Func1() {
          @Override public Reply call(Object data) {
            clearKeyIfNeeded(configProvider);
            if (data == null) {
              throw new RxCacheException(Locale.NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER
                  + " "
                  + configProvider.getProviderKey());
            }
            //        
            return new Reply(data, Source.CLOUD, configProvider.isEncrypted());
          }
        }).onErrorReturn(new Func1() {
          @Override public Object call(Object o) {
            throw new RxCacheException(Locale.NOT_DATA_RETURN_WHEN_CALLING_OBSERVABLE_LOADER
                + " "
                + configProvider.getProviderKey(), (Throwable) o);
          }
        });
      }

    使い方が変わる
    /**
         *
         * @param update     ,     true,        ,         ,        
         * @return
         */
        public Observable>> getUsers(int idLastUserQueried, final boolean update) {
            //    idLastUserQueried DynamicKey,
            return cacheProviders.getUsers(restApi.getUsers(idLastUserQueried, USERS_PER_PAGE), new DynamicKey(idLastUserQueried), new EvictDynamicKey(update));
        }

    例のソースコードli-MVPArmsはstartをあげます