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