這是一個儲存庫,其中包含在 Android 中使用 RxJava 的實際有用範例。它通常處於「正在進行中」(WIP) 的恆定狀態。
我還使用本儲存庫中列出的許多範例進行了有關學習 Rx 的演講。
using
)一個常見的要求是將冗長、繁重的 I/O 密集型操作卸載到後台執行緒(非 UI 執行緒),並在完成後將結果回饋給 UI/主執行緒。這是如何將長時間運行的操作卸載到後台執行緒的示範。操作完成後,我們恢復到主執行緒。全部使用RxJava!將此視為 AsyncTasks 的替代品。
長操作是透過阻塞 Thread.sleep 呼叫來模擬的(因為這是在後台執行緒中完成的,所以我們的 UI 永遠不會中斷)。
真正看到這個例子的光芒。多次點擊按鈕,看看按鈕點擊(這是一個 UI 操作)如何永遠不會被阻止,因為長操作僅在背景運行。
這是如何使用「緩衝區」操作累積事件的示範。
提供了一個按鈕,我們累積一段時間內該按鈕的點擊次數,然後得出最終結果。
如果您點擊該按鈕一次,您將收到一則訊息,說明該按鈕已點擊一次。如果您在 2 秒內連續點擊該按鈕 5 次,那麼您會得到一條日誌,表示您點擊該按鈕 5 次(對比 5 個單獨的日誌,顯示「按鈕點擊一次」)。
筆記:
如果您正在尋找一種更簡單的解決方案,可以累積「連續」點擊而不是僅記錄一段時間內的點擊次數,請查看 EventBus 演示,其中使用了publish
和buffer
運算符的組合。如需更詳細的解釋,您也可以查看這篇文章。
這是一個演示如何以只尊重最後一個事件的方式吞併事件。一個典型的例子是即時搜尋結果框。當您鍵入“Bruce Lee”一詞時,您不想執行 B、Br、Bru、Bruce、Bruce、Bruce L 等搜尋。 」。
當您在輸入框中鍵入內容時,它不會在每次輸入字元變更時都發出日誌訊息,而是僅選擇最後發出的事件(即輸入)並記錄該事件。
這是RxJava中的debounce/throttleWithTimeout方法。
Square 的 Retrofit 是一個令人驚嘆的函式庫,有助於輕鬆連網(即使您還沒有跳到 RxJava,您確實應該檢查一下)。它與 RxJava 配合使用效果更好,這些都是使用 GitHub API 的範例,直接取自 Android 半神開發者 Jake Wharton 在 Netflix 的演講。您可以透過此連結觀看演講。順便說一句,我使用 RxJava 的動機是來自參加 Netflix 的演講。
(注意:您很可能很快就會達到 GitHub API 配額,因此如果您想經常執行這些範例,請傳送 OAuth 令牌作為參數)。
自動更新視圖是一件很酷的事情。如果您以前接觸過Angular JS,它們有一個非常漂亮的概念,稱為“雙向資料綁定”,因此當HTML 元素綁定到模型/實體物件時,它會不斷“偵聽”該實體上的更改,並且根據模型自動更新其狀態。使用本範例中的技術,您可以輕鬆地使用類似演示視圖模型模式的模式。
雖然這裡的範例非常簡單,但使用Publish Subject
實現雙重綁定的技術更有趣。
這是使用 RxJava 調度程式進行輪詢的範例。這在您想要不斷輪詢伺服器並可能獲取新資料的情況下非常有用。網路呼叫是“模擬的”,因此它會在返回結果字串之前強制延遲。
有兩種變體:
第二個例子基本上是指數退避的變體。
我們在這裡使用 RepeatWithDelay,而不是使用 RetryWithDelay。為了理解重試(何時)和重複(何時)之間的區別,我建議丹在這個主題上發表精彩的文章。
不使用repeatWhen
的延遲輪詢的另一種方法是使用鍊式巢狀延遲可觀察量。請參閱 ExponentialBackOffFragment 範例中的 startExecutingWithExponentialBackoffDelay。
指數退避是一種策略,根據某個產出的回饋,我們改變進程的速率(通常會減少重試次數或增加重試或重新執行某個行程之前的等待時間)。
這個概念透過例子更有意義。 RxJava 讓實作這種策略(相對)簡單。我感謝麥克提出這個想法。
假設您出現網路故障。明智的策略是不要每 1 秒重試一次網路呼叫。相反,隨著延遲的增加而重試是明智的(不……優雅!)。那麼你嘗試在第 1 秒執行網路調用,沒有骰子嗎? 10秒後再嘗試...是否定的? 20秒後嘗試,沒有cookie? 1分鐘後嘗試。如果這個東西還是不行的話,你就得放棄網路了喲!
我們使用 RxJava 和retryWhen
運算子來模擬此行為。
RetryWithDelay
程式碼片段禮貌:
另請查看輪詢範例,其中我們使用非常相似的指數退避機制。
指數退避策略的另一種變體是執行給定次數但具有延遲間隔的操作。因此,您從現在起 1 秒後執行某個操作,然後從現在起 10 秒後再次執行該操作,然後從現在起 20 秒後執行該操作。總共執行 3 次後,您將停止執行。
模擬這種行為實際上比以前的重試機制要簡單得多。您可以使用delay
運算子的變體來實現此目的。
.combineLatest
)感謝 Dan Lew 在零散的播客中給了我這個想法 - 第 4 集(大約 4:30)。
.combineLatest
可讓您在一個位置同時緊密地監視多個可觀察物件的狀態。示範的範例展示如何使用.combineLatest
來驗證基本表單。此表單有 3 個主要輸入被視為「有效」(電子郵件、密碼和號碼)。一旦所有輸入都有效,該表格將變為有效(下面的文字變為藍色:P)。如果不是,則會針對無效輸入顯示錯誤。
我們有 3 個獨立的可觀察對象,用於追蹤每個表單欄位的文字/輸入變更(RxAndroid 的WidgetObservable
可以方便地監視文字變更)。在從所有3 個輸入中註意到事件變更後,結果將被「組合」並評估表單的有效性。
請注意,檢查有效性的Func3
函數僅在所有 3 個輸入都收到文字變更事件後才會啟動。
當表單中有更多數量的輸入欄位時,此技術的價值變得更加明顯。否則,使用一堆布林值來處理它會使程式碼變得混亂並且難以理解。但是使用.combineLatest
所有邏輯都集中在一個漂亮的緊湊程式碼區塊中(我仍然使用布林值,但這是為了使範例更具可讀性)。
我們有兩個來源 Observables:磁碟(快速)快取和網路(新鮮)調用。通常,磁碟 Observable 比網路 Observable 快得多。但為了演示其工作原理,我們也使用了一個假的「較慢」磁碟快取來查看運算子的行為。
這是使用 4 種技術來演示的:
.concat
.concatEager
.merge
.publish
選擇器+合併+takeUntil第四種技術可能是您最終想要使用的技術,但了解技術的進展並理解原因是很有趣的。
concat
很棒。它從第一個 Observable(在我們的例子中是磁碟快取)中檢索訊息,然後從後續的網路 Observable 中檢索資訊。由於磁碟快取可能更快,因此一切都顯示良好,磁碟快取載入速度很快,一旦網路呼叫完成,我們就交換出「新鮮」結果。
concat
的問題是,直到第一個 Observable 完成之後,後續的 observable 才會開始。這可能是個問題。我們希望所有可觀察量同時開始,但以我們期望的方式產生結果。值得慶幸的是,RxJava 引入了concatEager
它正是這樣做的。它啟動兩個 Observable,但緩衝後一個 Observable 的結果,直到前一個 Observable 完成。這是一個完全可行的選擇。
但有時,您只想立即開始顯示結果。假設第一個可觀察量(由於某種奇怪的原因)需要很長時間才能運行完其所有項目,即使第二個可觀察量的前幾個項目已經通過線路傳輸,它也會被強制排隊。你不一定想「等待」任何 Observable。在這些情況下,我們可以使用merge
運算子。它在發出項目時將其交錯。這非常有效,一旦顯示結果就開始輸出。
與concat
運算子類似,如果您的第一個 Observable 始終比第二個 Observable 快,那麼您不會遇到任何問題。然而, merge
的問題是:如果由於某種奇怪的原因,快取或較慢的可觀察值在較新/較新的可觀察值之後發出一個項目,它將覆蓋較新的內容。按一下範例中的「合併(慢速磁碟)」按鈕以查看此問題的實際情況。 @JakeWharton 和 @swankjesse 的貢獻變為 0!在現實世界中,這可能很糟糕,因為這意味著新資料將被過時的磁碟資料覆蓋。
為了解決這個問題,您可以將合併與超級漂亮的publish
運算符結合使用,該運算符接受「選擇器」。我在一篇部落格文章中寫了這種用法,但我要感謝 Jedi JW 提醒我這種技巧。我們publish
網路可觀察物件並為其提供一個選擇器,該選擇器開始從磁碟快取中發出,直到網路可觀察物件開始發出。一旦網路 observable 開始發出,它就會忽略磁碟 observable 的所有結果。這是完美的,可以解決我們可能遇到的任何問題。
以前,我使用merge
運算符,但透過監視「resultAge」克服了結果被覆蓋的問題。如果您想了解這個舊的實現,請參閱舊的PseudoCacheMergeFragment
範例。
這是一個超級簡單明了的範例,它向您展示如何使用 RxJava 的timer
、 interval
和delay
運算子來處理許多您想要以特定時間間隔執行任務的情況。基本上對 Android TimerTask
說「不」。
此處展示的案例:
隨附的部落格文章可以更好地解釋此演示的詳細資訊:
在 Android 中使用 RxJava 時提出的一個常見問題是,「如果發生設定變更(活動輪替、語言區域設定變更等),我該如何恢復可觀察物件的工作?」。
此範例向您展示了一種策略,即。使用保留的片段。在閱讀了 Alex Lockwood 的這篇精彩文章後,我開始使用保留的片段作為「工人片段」。
點擊開始按鈕並根據需要旋轉螢幕;你會看到可觀察的從它停止的地方繼續。
本範例中使用的源可觀察量的「熱度」存在某些怪癖。查看我的部落格文章,其中我解釋了具體細節。
此後我使用另一種方法重寫了這個範例。雖然ConnectedObservable
方法有效,但它進入了「多播」領域,這可能很棘手(線程安全、.refcount 等)。另一方面,主題要簡單得多。您可以在此處看到它使用Subject
重寫。
我寫了另一篇關於如何思考主題的博文,其中我詳細介紹了一些內容。
Volley 是 Google 在 IO '13 上推出的另一個網路庫。 github 的一位好心公民貢獻了這個範例,因此我們知道如何將 Volley 與 RxJava 整合。
我在這裡簡單地使用了主題。老實說,如果您還沒有透過Observable
來取得專案(例如透過 Retrofit 或網路請求),那麼就沒有充分的理由使用 Rx 並使事情複雜化。
此範例基本上將頁碼傳送到主題,然後主題處理新增項目。請注意concatMap
的使用以及_itemsFromNetworkCall
中Observable<List>
的回傳。
為了好玩,我還提供了一個PaginationAutoFragment
範例,這個「自動分頁」不需要我們點擊按鈕。如果您了解前面範例的工作原理,那麼遵循起來應該很簡單。
這裡有一些其他奇特的實現(雖然我喜歡閱讀它們,但我沒有將它們用於我的現實世界應用程序,因為我個人認為沒有必要):
下面的 ascii 圖以華麗的方式表達了我們下一個範例的意圖。 f1、f2、f3、f4、f5 本質上是網路調用,在進行這些調用時,會返回未來計算所需的結果。
(flatmap)
f1 ___________________ f3 _______
(flatmap) | (zip)
f2 ___________________ f4 _______| ___________ final output
|
____________ f5 _______|
此範例的程式碼已由網路上的一位 Mr.skehlet 編寫。轉到程式碼要點。它是用純 Java (6) 編寫的,因此如果您理解了前面的範例,那麼它就很容易理解。當時間允許或我已經用完其他令人信服的例子時,我會在這裡再次刷新它。
這是一個示範.timeout
運算子用法的簡單範例。按鈕 1 將在逾時限制之前完成任務,而按鈕 2 將強制出現逾時錯誤。
請注意我們如何提供一個自訂 Observable 來指示如何在逾時異常下做出反應。
using
) using
業者相對鮮為人知,而且眾所周知很難透過 Google 搜尋。它是一個漂亮的 API,有助於設定(昂貴的)資源、使用它,然後以乾淨的方式處理掉。
這個運算符的好處在於,它提供了一種以嚴格範圍的方式使用可能昂貴的資源的機制。使用 -> 設定、使用和處置。想想資料庫連線(如 Realm 實例)、套接字連線、執行緒鎖等。
Rx 中的多播就像一門黑暗藝術。沒有多少人知道如何毫無顧慮地實現這一目標。此範例配置兩個訂閱者(以按鈕的形式),並允許您在不同時間點新增/刪除訂閱者,並查看不同電信商在這些情況下的行為。
來源 observale 是一個計時器( interval
)observable,選擇它的原因是有意選擇一個非終止 observable,這樣您就可以測試/確認您的多播實驗是否會洩漏。
我還在 360|Andev 上詳細介紹了多播。如果您有興趣和時間,我強烈建議您先觀看該演講(特別是多播運算符排列部分),然後再嘗試此處的範例。
這裡的所有範例都已移轉到使用 RxJava 2.X。
在某些情況下,我們使用 David Karnok 的 Interop 函式庫,因為某些函式庫(如 RxBindings、RxRelays、RxJava-Math 等)尚未移植到 2.x。
我盡力確保這些範例不會過度做作,而是反映現實世界的用例。如果您有類似的有用範例示範 RxJava 的使用,請隨時發送拉取請求。
我也正在研究 RxJava,所以如果您覺得有更好的方法來完成上述範例之一,請提出一個問題來解釋如何做。更好的是,發送拉取請求。
Rx 線程是一件很混亂的事情。為了提供協助,該專案使用 YourKit 工具進行分析。
YourKit 透過創新和智慧的工具支援開源項目,用於監控和分析 Java 應用程式。 YourKit 是 YourKit Java Profiler 的創作者。
根據 Apache 授權 2.0 版(「授權」)取得授權。您可以在以下位置取得許可證副本:
http://www.apache.org/licenses/LICENSE-2.0
除非適用法律要求或書面同意,否則根據許可證分發的軟體均以「原樣」分發,不帶任何明示或暗示的保證或條件。請參閱許可證,了解許可證下管理權限和限制的特定語言。
您同意對此儲存庫的所有貢獻(以修復、拉取請求、新範例等形式)均遵循上述許可。