2017-12-20 14 views
2

私は次のようなシナリオを持っています:Fooというオブジェクトがあります。このFooオブジェクトはリモートサービスから来ており、このインスタンスをローカルにキャッシュしたいと思います。一定の時間が経過するまで使用してください。これまでのところ、私が来ている今RxJava:タイムスタンプが期限切れになるまでキャッシュから読み取る

public class FooState { 

    private Foo foo; 
    private long timestamp; 

    /* Constructor and getters */ 

} 

、:これまでのところ、私はFooはミリ秒単位でフェッチされた時刻を示すタイムスタンプと一緒にFooのインスタンスが含まれているFooStateクラスを作成しようとしました

連結を使用しています。このコードで、最大:キャッシュされた値があるかどう

public Observable<Foo> foo() { 
    return Observable.concat(local(), remote()) 
       .takeFirst(fooState -> fooState.getTimestamp() >= System.currentTimeMillis()) 
       .map(fooState -> fooState.getFoo()) 
       .defaultIfEmpty(new Foo()); 

} 

private Observable<FooState> local() { 
    return Observable.just(cache.hasFooState() ? cache.getFooState() : new FooState(null, 0)); 
} 

private Observable<FooState> remote() { 
    return api.getFoo() 
       .map(foo -> new FooState(foo, System.currentTimeMillis() + ONE_DAY_MILLIS) 
       .doOnNext(fooState -> { 
        cache.save(fooState); 
       }); 
} 

は基本的に、私がいる限り、タイムスタンプの有効期限が切れていないとして、それを使用したいです。タイムスタンプが期限切れになっているか、キャッシュされた値がない場合は、リモートサービスからフェッチして結果をキャッシュします。

このユースケースを実装するクリーナーの方法はありますか?私はRxJavaにはまったく新しく、Rx-gurusがこのシナリオを処理するためのより良い方法を知っているかどうか疑問に思っていました。

答えて

0

また、内蔵の.timestamp()オペレータとタイムスタンプ付き<T>クラスを使用することができます。

BehaviorSubject<Timestamped<Foo>> subject = BehaviorSubject.create(); 

    Observable<Foo> serviceCall() { 
     return subject.filter(new Func1<Timestamped<Foo>, Boolean>() { 
      @Override 
      public Boolean call(Timestamped<Foo> tsFoo) { 
       return tsFoo.getTimestampMillis() < expiry; 
      } 
     }).switchIfEmpty(serviceCall().timestamp().doOnNext(new Action1<Timestamped<Foo>>() { 
      @Override 
      public void call(Timestamped<Foo> tsFoo) { 
       subject.onNext(tsFoo); 
      } 
     }) 
     .map(new Func1<Timestamped<Object>, Foo>() { 
      @Override 
      public Foo call(Timestamped<Object> tsFoo) { 
       return tsFoo.getValue(); 
      } 
     }); 
    } 
0

私があれば地元から何かを放出しないであろう(RxJava 2)このユースケースのために最も適切であると思われるようconcatであなたのアプローチに似た何かを思い付いたが、Maybeに基づいて私のソリューションを構築キャッシュが存在しないか、キャッシュエントリが期限切れになっています。

public class RxJavaUnitTestJava { 
    public class Foo extends Object { 
     String source = "n/a"; 
    } 

    public class FooState { 
     private Foo foo; 
     private long timestamp; 

     public FooState(Foo foo, long timestamp) { 
      this.foo = foo; 
      this.timestamp = timestamp; 
     } 
    } 

    private static long EXPIRATION = 0l; 
    private FooState cachedFooState = null; 

    private Maybe<Foo> local() { 
     return Maybe.fromCallable(() -> { 
      System.out.println("checking local"); 

      if (cachedFooState != null && cachedFooState.timestamp + EXPIRATION > System.currentTimeMillis()) { 
       System.out.println("taking unexpired local"); 
       cachedFooState.foo.source = "local"; // mark source of this foo 

       return cachedFooState.foo; 
      } else { 
       System.out.println("not taking local"); 
       return null; 
      } 
     }); 
    } 

    private Maybe<Foo> remote() { 
     return Maybe.fromCallable(() -> { 
      System.out.println("checking remote"); 

      Foo foo = new Foo(); 
      foo.source = "remote"; // mark source of this foo 
      FooState fooState = new FooState(foo, System.currentTimeMillis()); 
      cachedFooState = fooState; 

      return fooState.foo; 
     }); 
    } 

    private Observable<Foo> foo() { 
     return Maybe.concat(local(), remote()) 
       .take(1) 
       .toObservable(); 
    } 

    @Test 
    public void testNoCachedLocal() { 
     cachedFooState = null; 

     foo() 
       .doOnNext(foo -> System.out.println("doOnNext: foo.source: " + foo.source)) 
       .doOnComplete(() -> System.out.println("onComplete")) 
       .test() 
       .assertValueCount(1) 
       .assertComplete(); 
    } 

    @Test 
    public void testExpiredLocal() { 
     cachedFooState = new FooState(new Foo(), System.currentTimeMillis()); 
     EXPIRATION = 0l; 

     foo() 
       .doOnNext(foo -> System.out.println("doOnNext: foo.source: " + foo.source)) 
       .doOnComplete(() -> System.out.println("onComplete")) 
       .test() 
       .assertValueCount(1) 
       .assertComplete(); 
    } 

    @Test 
    public void testUnExpiredLocal() { 
     cachedFooState = new FooState(new Foo(), System.currentTimeMillis()); 
     EXPIRATION = TimeUnit.SECONDS.toMillis(30); 

     foo() 
       .doOnNext(foo -> System.out.println("doOnNext: foo.source: " + foo.source)) 
       .doOnComplete(() -> System.out.println("onComplete")) 
       .test() 
       .assertValueCount(1) 
       .assertComplete(); 
    } 
} 
関連する問題