我正在使用Retorfit
+ RxJava2
制作网络,我希望将响应缓存30秒。 30秒间隔后进行的任何调用都应该从服务器获得最新结果。我尝试使用Replay
运算符执行此操作,但每次调用subscribe时它仍会进行网络调用。我不是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
级别缓存它,它将缓存策略应用于通过它进行的所有请求。最后,我想在需要时为不同的请求定义不同的缓存过期。并非我的所有请求都需要缓存一小段时间。我想知道我是否能做到这一点。
感谢您的帮助。
答案 0 :(得分:2)
你的方法有两个问题:
Observable
并创建新的service.getName()
实例时,您都会创建新的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()
) - 意味着请求将是只执行一次。
在缓存过期后,附加订阅将触发新请求,并将设置新计时器以重置缓存过期。
答案 1 :(得分:1)
尝试查看okhttp拦截器。
添加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();