2016-11-22 103 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 

問題

我希望所有的觀測將只將原始列表傳遞給用戶我總是得到原始對象,並且對該對象所做的所有更改都將反映在基本列表中,因此最後,空的可觀察對象應該返回2個加載的數據項,但對於此嵌套緩存方案並不適用。

我需要什麼

  • 每個操作應該只運行一次!無論是否有人訂閱了可觀察的內容!這是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

我現在讀的鏈接,它看起來像'重播()。AUTOCONNECT()'可能是什麼,我需要一個solutuion(我需要緩存,我想每個步驟只需執行一次+我需要共享可觀察數據),但這會導致與我發佈的解決方案相同的結果。您的建議將不會執行並且已經在第一次調用getEmptyData()時阻塞。根據你的建議,我試着使用'share().replay()。autoConnect()'進行緩存和共享(這就是你想表達的想法),但是這又產生了與我發佈的解決方案相同的結果。 – prom85

+0

你對包含'autoConnect'是正確的。儘管如此,您仍然可能會遇到訂購問題。你嘗試過'share()。autoConnect().replay()'? – JohnWowUs

+0

'share()'不返回'ConnectableObserver' ...所以不行,因爲這是不可能的。我嘗試了我能想到的所有事情,大部分時間我都能得到相同的結果......只有我能想到的解決方案是處理緩存的自定義數據結構(我過去已經使用了類似的東西),但我認爲這應該沒有必要... – prom85