2017-04-03 6 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'が返されます。その 'replay'演算子、およびそれを好む他のものは、単一のObservableでさまざまな種類のマルチキャスティングを可能にすることを意図しています。ほとんどの場合、 'service.getName()'メソッドの後ろにキャッシングを実装したいと思うでしょう。それは、それを実行するために作成するObservableの数にかかわらずキャッシング機能を提供します。これは、Httpクライアント層、「Cache-Control」などの方法でキャッシュを実装することを意味します。 – drhr

+0

1つの解決策は、そのコールに対してのみ異なる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の問題です:@drhrとして

  1. service.getName()と呼ぶたびに新しいObservableを作成すると、新しいインスタンスを作成していますeがObservableの場合、同じ再生インスタンスを保持し、service.getName()を呼び出すたびに同じインスタンスの外に呼び出し元に渡す必要があります。
  2. replayを30秒で返しても、最後の30秒に渡ってソースObservableが発行したシーケンスが再生されます。つまり、キャッシュの有効期限が過ぎると、リクエストが30秒を超えても何も起こりません前。この期間の後にObservableが自動的に再起動することを意味するものではありません。

特定の期間キャッシュするには、キャッシュ期間後にキャッシュされたレスポンスを無効にし、この期間後に新しいリクエストを実行する必要があります。つまり、購読を制御してそこで行う必要があります。
あなたはそのような何かでそれを達成することができます:あなたはので、すべてがキャッシュの有効期限内に起こったサブスクライブ、キャッシュの有効期限の状態に応じて、右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; 
     }); 
    } 
} 

cache()を使用して)Observableをキャッシュされます - 意味リクエストは1回だけ実行されます。 キャッシュ満了後、追加のサブスクリプションが新しい要求をトリガーし、キャッシュの有効期限をリセットする新しいタイマーを設定します。

+0

この解決法はかなり素晴らしいようです。それを試してみましょう。応答していただきありがとうございます。 – Vab

+0

私はあなたのソリューションを試しましたが、動作していません。私はそれをデバッグし、それはキャッシュされた観測可能だが、依然としてネットワーク要求をしているようだ。助けてください。 ' ' public class UserNameService { CachedRequest obs; public Observable getUserName(){ if(obs == null){ Observable observable = RetrofitBuilder.getInstance()。グループリスト(); obs =新しいCachedRequest <>(observable、1、TimeUnit.MINUTES); } return obs.getCachedObservable().subscribeOn(Schedulers.io()); } } – Vab

+0

どのように正確に使用しますか? CachedObservableの作成はマルチスレッドに対して保護されていませんが、それがあなたのケースで問題であるかどうか疑問です。 – yosriz

関連する問題