2016-11-22 1 views
0

문제점을 재현하는 작은 테스트를 작성했습니다. 캐시 된 observables를 중첩시키고 원본 데이터 구조에 대한 모든 변경 사항을 ORIGINAL 객체에 반영하려고하지만 중첩 된 캐싱을 사용하면이 작업이 실패한 것으로 보입니다.캐시 된 rx java observables 데이터 업데이트 (중첩 된 캐싱 사용)

문제를 해결할 수 있습니까? 내가

테스트

public static void test() 
{ 
    // -------------------- 
    // TEST 1 - simple caching 
    // -------------------- 

    List<Data> emptyData1 = getEmptyData() 
      .toBlocking() 
      .single(); 

    L.d("TEST 1a - Size: %d == %d", emptyData1.size(), 0); 

    // add 1 empty data 
    emptyData1.add(new Data(0, false)); 
    L.d("TEST 1b - Size: %d == %d (Loaded: %b)", emptyData1.size(), 1, emptyData1.get(0).loaded); 

    List<Data> emptyData2 = getEmptyData() 
      .toBlocking() 
      .single(); 

    L.d("TEST 1c - Size: %d == %d (Loaded: %b)", emptyData2.size(), 1, emptyData2.get(0).loaded); 

    // -------------------- 
    // TEST 2 - nested caching 
    // -------------------- 

    List<Data> loadedData1 = getLoadedData() 
      .toBlocking() 
      .single(); 

    L.d("TEST 2a - Size: %d == %d (Loaded: %b)", loadedData1.size(), 1, loadedData1.get(0).loaded); 

    // add a second data, this time we add a loaded data 
    loadedData1.add(new Data(1, true)); 

    List<Data> loadedData2 = getLoadedData() 
      .toBlocking() 
      .single(); 

    L.d("TEST 2b - Size: %d == %d (Loaded: %b, %b)", loadedData1.size(), 2, loadedData2.get(0).loaded, loadedData2.get(1).loaded); 

    // -------------------- 
    // TEST 3 - test if empty observable is holding the loaded data as well now 
    // -------------------- 

    List<Data> testEmptyStateData = getEmptyData() 
      .toBlocking() 
      .single(); 

    L.d("TEST 3a - Size: %d == %d", testEmptyStateData.size(), 2); 

    // I don't expect this, but this will happen 
    if (testEmptyStateData.size() == 1) 
    { 
     L.d("TEST 3b1 - Size: %d == %d (Loaded: %b)", testEmptyStateData.size(), 2, testEmptyStateData.get(0).loaded); 
    } 
    // I expect this, but it won't be true 
    if (testEmptyStateData.size() == 2) 
    { 
     L.d("TEST 3b - Size: %d == %d (Loaded: %b, %b)", testEmptyStateData.size(), 2, testEmptyStateData.get(0).loaded, testEmptyStateData.get(1).loaded); 
    } 
} 

결과 ... 내가 문제에서 무엇을 기대

TEST 1a - Size: 0 == 0 
TEST 1b - Size: 1 == 1 (Loaded: false) 
TEST 1c - Size: 1 == 1 (Loaded: false) 

TEST 2a - Size: 1 == 1 (Loaded: true) 
TEST 2b - Size: 2 == 2 (Loaded: true, true) 

TEST 3a - Size: 1 == 2 // <= UNDESIRED RESULT!!! 
TEST 3b1 - Size: 1 == 2 (Loaded: true) // <= at least the object in the list is correct! But I would expect that the empty observalbe would hold 2 items, both loaded! "Test 3b2" should be printed instead 

문제

내가 기대를 작성한 모든 관찰 가능한 다만 것 ORIGINAL리스트를지나 가게해라. 오전 및 나는 항상 원래 개체를 얻고이 개체에 대한 내 모든 변경 내용이 기본 목록에 반영되므로 결국 빈 관찰 항목은로드 된 두 개의 데이터 항목을 반환해야하지만이 중첩 된 캐싱 시나리오에는 맞지 않습니다. .

내가

  • 각 작업은 한 번만 실행해야합니다 필요한 것! 누군가가 관측 가능 여부에 관계없이 상관없이! 즉 whyI가 공유해야 cache()
  • 관찰 가능한을 사용하기로 결정하고

코드

도우미 기능과 데이터

private static Observable<List<Data>> mEmptyObservable = null; 
private static Observable<List<Data>> mLoadedObservable = null; 

public static Observable<List<Data>> getEmptyData() 
{ 
    if (mEmptyObservable == null) 
    { 
     // simple test data 
     List<Data> values = new ArrayList<>(); 
     mEmptyObservable = Observable.just(values) 
       // cache and share observable 
       .cache().replay().refCount(); 
    } 
    return mEmptyObservable; 
} 

public static Observable<List<Data>> getLoadedData() 
{ 
    if (mLoadedObservable == null) 
    { 
     mLoadedObservable = getEmptyData() 
       .flatMap(new Func1<List<Data>, Observable<Data>>() 
       { 
        @Override 
        public Observable<Data> call(List<Data> datas) 
        { 
         return Observable.from(datas); 
        } 
       }) 
       .map(new Func1<Data, Data>() 
       { 
        @Override 
        public Data call(Data data) 
        { 
         data.load(); 
         return data; 
        } 
       }) 
       .toList() 
       // cache and share observable 
       .cache().replay().refCount(); 
    } 
    return mLoadedObservable; 
} 

데이터 클래스

을 재사용 할 수 있어야합니다입니다
static class Data 
{ 
    int index; 
    boolean loaded; 

    public Data(int index, boolean processed) 
    { 
     this.index = index; 
     this.loaded = processed; 
    } 

    public void load() 
    { 
     if (!loaded) 
     { 
      // do some have operation... once only per data! 
     } 
     loaded = true; 
    } 
} 

답변

1

cache()replay()을 모두 사용하고 있습니다. 같은 것을 시도하십시오

private static Observable<List<Data>> mEmptyObservable = null; 
private static Observable<List<Data>> mLoadedObservable = null; 

public static Observable<List<Data>> getEmptyData() 
{ 
    if (mEmptyObservable == null) 
    { 
     // simple test data 
     List<Data> values = new ArrayList<>(); 
     mEmptyObservable = Observable.just(values) 
       // share and replay observable 
       .share().replay(); 
    } 
    return mEmptyObservable; 
} 

public static Observable<List<Data>> getLoadedData() 
{ 
    if (mLoadedObservable == null) 
    { 
     mLoadedObservable = getEmptyData() 
       .flatMap(new Func1<List<Data>, Observable<Data>>() 
       { 
        @Override 
        public Observable<Data> call(List<Data> datas) 
        { 
         return Observable.from(datas); 
        } 
       }) 
       .map(new Func1<Data, Data>() 
       { 
        @Override 
        public Data call(Data data) 
        { 
         data.load(); 
         return data; 
        } 
       }) 
       .toList() 
       // share and replay observable 
       .share().replay(); 
    } 
    return mLoadedObservable; 
} 

더 많은 정보가 this answer입니다.

편집 :하지만 진짜 문제는 getLoadedData()에서 다른 목록을 만드는 것 같습니다. 같은 목록 내가 지금 링크를 읽은

public static Observable<List<Data>> getLoadedData() 
{ 
    if (mLoadedObservable == null) 
    { 
     mLoadedObservable = getEmptyData(). 
       .map(new Func1<List<Data>, List<Data>>() 
       { 
        @Override 
        public Data call(List<Data> data) 
        { 
         for (Data item : data) { 
          item.load(); 
         } 
         return data; 
        } 
       }) 
       // share and replay observable 
       .share().replay(); 
    } 
    return mLoadedObservable; 
} 
+0

같은 것을 시도하고자하고 재생()'처럼 보인다합니다. 자동 연결()가'내가 필요로 무엇을위한 solutuion 수 (내가 원하는, 캐싱이 필요 각 단계는 한 번만 실행하고 관찰 가능 항목을 공유해야합니다.) 그러나 이것은 내가 게시 한 솔루션과 동일한 결과로 이어질 것입니다. 당신의 제안은'getEmptyData()'에 대한 첫 번째 호출에서 이미 실행되고 차단되지 않습니다. 귀하의 제안에 따라'share(). replay(). autoConnect() '를 사용하여 캐싱과 공유를 시도했으나 (제안한 바였습니다), 다시 한 번 제가 게시 한 솔루션과 동일한 결과가 나타납니다. – prom85

+0

당신은'autoConnect'를 포함하는 것이 옳습니다. 그래도 주문 문제가있을 수 있습니다. 'share(). autoConnect(). replay()'를 시도 했습니까? – JohnWowUs

+0

'share()'는'ConnectableObserver'를 리턴하지 않습니다 ... 불가능합니다. 내가 생각할 수있는 모든 것을 시도했다. 대부분 같은 시간에 동일한 결과를 얻는다. 내가 생각할 수있는 유일한 해결책은 캐싱을 처리하는 사용자 정의 데이터 구조이다 (나는 이미 과거에 이것을 사용했다).하지만 나는 생각한다. 이것은 필요하지 않아야합니다 ... – prom85