使用 RxJava 從多種來源中載入資料

我是魚小小魚發表於2016-06-26

假設我需要從網路上查詢一些資料。顯然我可以在每次需要時都從網路上查詢,但是將資料快取在硬碟和記憶體中將更加高效。

具體點說,我想要這麼做:

1.只有在需要從伺服器更新資料的時候,才通過網路查詢。

2.其他情況則從通過快取之前查詢的資料,實現快速的讀取。

接下來,我將通過使用 RxJava 來實現上面的想法。

基本模式

為每一個資料來源(網路、磁碟和記憶體)準備一個Observable<Data> ,然後我們就可以通過使用concat() 和 first() 這兩種操作來構建一個簡單的解決方案。

concat() 可以把多個Observables的結果連結起來。而first() 可以取出一個序列中的第一項。因此,你可以使用concat().first()來取出多個資料來源所組成序列的第一項。

讓我們通過程式碼來看一下:

這個模式的關鍵點在於,concat()只會在需要時才去訂閱每個Observable。如果資料已經被快取了,first() 就會停止查詢,即不再去較慢的資料來源中查詢。換種更具體的說法,如果記憶體中返回了資料結果,我們就不必再去磁碟或者網路中查詢了。相反,如果記憶體和磁碟都不返回結果,就會觸發一次新的網路請求。

請注意,在concat()中不同Observable的放置順序是非常重要的,因為在查詢時,是按照指定的順序進行的。

資料儲存

接下來,我們需要對查詢到的資料進行儲存。如果你不把網路查詢的資料結果儲存到磁碟,也不把磁碟查詢的結果快取到記憶體中,那之前的工作就白瞎啦!我們在上面寫的程式碼會一直通過網路查詢資料。

我的解決方案是:每當一個資料來源返回結果,就把該結果儲存或者快取。

現在,當你使用networkWithSave 和 diskWithCache載入資料時,這些資料都會同時被儲存起來。

採用這種策略還有一個好處,networkWithSave/diskWithCache 可以在任何地方使用,而不僅限在我們的多資料來源模式中。

資料陳舊

不幸的是,我們目前的資料儲存方案效率有點高的過頭了。每次都會返回相同的資料,而這些資料都不知道是幾年前的了。注意,在伺服器更新資料後,我們是需要通過網路查詢新資料的。

這個問題的解決辦法存在於first()中,可以通過它來進行過濾操作,即拒絕返回沒有價值的資料。

這樣一來,我們就只會得到序列中最快且最新的資料。如果某個資料來源中的資料不是最新的,那麼就會在它後面的資料來源中查詢,直到找到最新的資料。

first() vs. takeFirst()

除了使用first(),我們還有一種選擇,即takeFirst()

兩種方法間的區別是當所有資料來源都無法返回有效資料時,first()會丟擲NoSuchElementException() 異常,而takeFirst()會正常結束,不丟擲異常。

至於具體使用哪一個方法,主要取決於你是否需要顯式地處理資料的缺失。

程式碼樣例

下面的連結是上述程式碼的完整實現:https://github.com/dlew/rxjava-multiple-sources-sample

如果你想要學習真實的案例,可以進入the Gfycat app , 在這裡使用了文章中介紹的模式以獲取Gfycat的後設資料。所涉及的程式碼並沒有用到文章中介紹的所有能力(因為並不需要),但是它向我們展示了concat().first()的基本使用。

相關文章