2017-04-03 54 views
0

我正在使用Retorfit + RxJava2网络,我想缓存响应30秒。间隔30秒后进行的任何呼叫应从服务器获取最新结果。我尝试使用Replay运营商来完成此操作,但每次我呼叫订阅时仍会进行网络呼叫。我不是RxJava的专家,所以也许我对使用Replay进行缓存的理解是错误的。缓存使用RxJava一段时间的网络调用

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

UPDATE:我的道歉,如果我没有解释清楚我的问题。我想要的是缓存一个特定的请求,而不是缓存在HttpClient级别,它将缓存策略应用于所有通过它的请求。最后,我想在需要时为不同的请求定义不同的缓存过期。并非我所有的请求都需要缓存很长时间。我想知道我能否做到这一点。

感谢您的帮助。

+0

每次调用'service.getName()'都会返回一个新的'Observable'。这个“重放”操作符以及其他类似的操作符意味着允许在单个Observable上进行多种多播。最有可能的是,你希望在'service.getName()'方法的后面实现缓存,这样它将提供缓存功能,无论你创建多少个独立的Observable运行它。这可能意味着你使用“Cache-Control”或其他方法在Http客户端层实现缓存。 – drhr

+0

一个解决方案是仅为该呼叫使用不同的okhttp客户端/拦截器。 – parkgrrr

回答

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

看起来这会将此构建器发出的所有请求缓存30秒。另外,如果我对不同的请求需要不同的期满,我必须在这种情况下创建多个'OkHttpClient'。如果我可以缓存缓存过期的特定请求,那将会很好。 – Vab

+0

@vab好像你还没有写关于这个新的条件 – shmakova

+0

我已经在我的问题作了一些修改如你所说。感谢您指出。 – Vab

2

的2个问题,你的方法:

  1. 为@drhr提到,您每次拨打电话service.getName()时都会创建一个新的Observable您正在创建一个新实例e的Observable,则应保持相同的重播实例,并在每次调用service.getName()时向同一实例外的调用方提供。
  2. 即使您将以30秒返回同一个实例replay,也会在过去的30秒内重播源Observable发出的序列,这意味着在缓存过期时间后,您的请求发生时间超过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())获取缓存 - 意义请求将只执行一次。 缓存过期后,额外的订阅将触发新的请求,并将设置一个新的计时器来重置缓存过期。

+0

这个解决方案看起来非常棒。让我试试这个,回到你身边。感谢您的回应。 – Vab

+0

我试过你的解决方案,但它不工作。我调试它,它似乎正在缓存观察,但仍然提出网络请求。请帮忙。 '' 公共类UserNameService { CachedRequest OBS; ()obj == null { Observable observable = RetrofitBuilder.getInstance()。groupList(); obs = new CachedRequest <>(observable,1,TimeUnit.MINUTES); } 返回obs.getCachedObservable().subscribeOn(Schedulers.io()); } }' – Vab

+0

你如何使用它是什么呢?该CachedObservable的创作没有人看守对多线程的,但我怀疑,如果这是我使用的正是我在上面评论表示同样的方式,你的情况 – yosriz

相关问题