Кэширование сетевых вызовов с использованием RxJava на некоторое время

Я делаю сеть, используя Retorfit + RxJava2 и я хочу кешировать ответ в течение 30 секунд. Любые звонки, сделанные через 30 секунд, должны получать самые последние результаты с сервера. Я пытался сделать это с помощью Replay оператор, но он все еще делает сетевой вызов каждый раз, когда я звоню подписаться. Я не эксперт в RxJava, так что, возможно, мое понимание использования Replay за кеширование, как это неправильно.

public Observable<Name> getName() {
        return retrofitBuilder.getName()
                .subscribeOn(Schedulers.io())
                .replay(30, TimeUnit.SECONDS,Schedulers.io())
                .autoConnect();
    }

и я называю приведенный выше код следующим образом:

 service.getName()
        .subscribe(new Consumer<Name>()
            {
                @Override
                public void accept(Name name) throws Exception
                {
                    Log.d("getName", "Name: " + name.toString());
                }
            }
            , new Consumer<Throwable>()
            {
                @Override
                public void accept(Throwable throwable) throws Exception
                {
                    Log.d("getName", throwable.getMessage());
                }
            });

ОБНОВЛЕНИЕ: мои извинения, если я не объяснил свой вопрос ясно. То, что я хочу, это кэширование на конкретный запрос, а не на HttpClient уровень, который применяет стратегию кэширования ко всем запросам, которые выполняются через него. В конце я хотел бы определить другой срок действия кэширования для другого запроса, когда это необходимо. Не все мои запросы нуждаются в кэшировании для небольшой продолжительности. Мне было интересно, смогу ли я сделать это.

Ценю вашу помощь в этом.

2 ответа

Есть 2 проблемы с вашим подходом:

  1. как уже упоминалось @drhr, вы создаете новый Observable каждый раз, когда вы звоните service.getName() вы создаете новый экземпляр Observable, вы должны сохранять один и тот же воспроизводимый экземпляр и давать вызывающей стороне вне одного и того же экземпляра каждый раз, когда он вызывает service.getName(),
  2. даже если вы вернете тот же экземпляр, replay с 30 секунд, будет воспроизводить последовательность, излучаемую источником Observable в течение последних 30 секунд, то есть после истечения срока действия кэша вы ничего не получите, поскольку ваш запрос произошел более 30 секунд назад. это не значит, что Observable перезапустится автоматически после этого периода.

Чтобы кешировать данные за определенный период, вам необходимо аннулировать кэшированный ответ после периода кеширования и выполнить новый запрос после этого периода, то есть вы должны контролировать свою подписку и делать это там.
Вы можете достичь этого с помощью чего-то такого:

public class CachedRequest<T> {

    private final AtomicBoolean expired = new AtomicBoolean(true);
    private final Observable<T> source;
    private final long cacheExpirationInterval;
    private final TimeUnit cacheExpirationUnit;
    private Observable<T> current;

    public CachedRequest(Observable<T> o, long cacheExpirationInterval,
                         TimeUnit cacheExpirationUnit) {
        source = o;
        current = o;
        this.cacheExpirationInterval = cacheExpirationInterval;
        this.cacheExpirationUnit = cacheExpirationUnit;
    }

    private Observable<T> getCachedObservable() {
        return Observable.defer(() -> {
            if (expired.compareAndSet(true, false)) {
                current = source.cache();
                Observable.timer(cacheExpirationInterval, cacheExpirationUnit)                          
                        .subscribe(aLong -> expired.set(true));
            }
            return current;
        });
    }
}

с отсрочкой вы можете вернуть право Observable в соответствии со статусом срока действия кэша, поэтому каждая подписка, произошедшая в течение срока действия кэша, будет кэшироваться Observable (с помощью cache()) - смысл запроса будет выполнен только один раз. после окончания срока действия кэша дополнительная подписка вызовет новый запрос и установит новый таймер для сброса срока действия кэша.

Попробуйте посмотреть на охтп перехватчики.

Добавить CacheInterceptor:

public class CacheInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());

        CacheControl cacheControl = new CacheControl.Builder()
                .maxAge(30, TimeUnit.SECONDS)
                .build();

        return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", cacheControl.toString())
                .build();
    }
}

И добавьте его и кэшируйте в свой клиент OkHttp следующим образом:

File httpCacheDirectory = new File(context.getCacheDir(), "http-cache");
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(httpCacheDirectory, cacheSize);

OkHttpClient httpClient = new OkHttpClient.Builder()
                               .addNetworkInterceptor(new CacheInterceptor())
                               .cache(cache)
                               .build();
Другие вопросы по тегам