使用 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 个问题:

  1. 如@drhr所述,每次调用service.getName()创建新实例Observable时,您都会创建一个新Observable,您应该保留相同的重播实例,并在每次调用service.getName()时提供给同一实例外部的调用方。
  2. 即使您将返回相同的实例(replay30 秒),也会重播源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;
});
}
}

使用defer,您可以根据缓存过期状态返回正确的Observable,因此在缓存过期期间发生的每个订阅都将被缓存Observable(使用cache()) - 这意味着请求将只执行一次。 缓存过期后,其他订阅将触发新请求,并将设置新的计时器来重置缓存过期。

尝试查看 okhttp 拦截器。

添加缓存拦截器:

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

相关内容

  • 没有找到相关文章

最新更新