天天看點

[譯] RxJava 中的錯誤處理

<b>本文講的是[譯] RxJava 中的錯誤處理,</b>

<b></b>

Drawing

一旦你開始使用 RxJava 函數庫寫代碼,你會發現一些東西能有很多不同的實作方式,但是有時你很難立即确定哪種方法最好,錯誤處理就是其中之一。

那麼,在 RxJava 中處理錯誤的最佳方法是什麼,又有哪些選擇呢?

假設你有一個 Observable 可能會産生異常。如何處理呢?第一反應應該是直接在 <code>onError</code>消費者中處理錯誤。

它類似于我們以前使用的 <code>AsyncTasks</code>,并且看起來很像一個 try-catch 塊。

這兒有一個大問題。 假設在 <code>userProvider.getUsers()</code> Observable 中存在程式設計錯誤,導緻<code>NullPointerException</code> 或類似的異常。如果能夠立刻崩潰的話就好了,我們可以現場檢測出問題并且解決。然而上面的代碼中我們無法看到崩潰,因為錯誤被 onError 處理了,它隻會顯示一個錯誤資訊或者其他結果。

更糟糕的是,測試時不會有任何崩潰。測試會失敗,并伴随着神秘且意想不到的行為。你不得不花時間調試,而不是立即在一個直覺/具體的棧中找到原因。

首先聲明,解釋下我所謂的預期中的和非預期中的異常。

可預期異常不是說代碼出 bug,而是指運作環境有問題。比如各種 IO 異常,無網絡異常等。你的軟體應該适當的對這些異常産生反應,或者顯示錯誤消息等。預期的異常類似于第二個有效的傳回值,它們是方法簽名的一部分。

非預期的異常大多是程式設計錯誤。它們可以并且将會在開發的時候出現,但是它們永遠不應該發生在生産環境中。至少這是一個目标。但是如果它們确實發生了,通常立即使應用崩潰是一個好主意。這有助于提高問題的關注度然後盡快修複之。

在 Java 中,預期中的異常大多是使用受檢異常(直接從 <code>Exception</code> 類子類化)實作的。而大多數預期之外的異常則是使用從 <code>RuntimeException</code> 類派生的未受檢異常實作的。

是以,如果我們想要崩潰,為什麼不檢查異常是否是一個 <code>RuntimeException</code>,并在 <code>onError</code>消費者内重新抛出它呢?如果不僅僅像之前的例子那樣處理它呢?

這可能看起來不錯,但它有一些缺陷:

在 RxJava 2 中,非常令人費解的是它會在實時運作的應用中崩潰,而在測試中不會。在 RxJava 1 中,則無論實時運作還是測試都會崩潰。

我們想要崩潰的,除了 <code>RuntimeException</code> 之外還有更多未受檢異常,這包括 <code>Error</code>等。很難追蹤所有的這類異常。

但主要缺點是這樣的:

在應用開發過程中,你的 Rx 鍊将會變得越來越複雜。你的 Observable 也将會在不同的地方被重用,包括你從沒料到會使用到的上下文中。

假設你已經決定在這個鍊中使用 <code>userProvider.getUsers()</code> 這個 Observable:

當兩個 <code>userProvider.getUsers()</code> 都觸發一個錯誤将會發生什麼?

現在,你可能認為這兩個錯誤都分别映射到一個空清單上,是以将會有兩個空清單被觸發。不過你可能會驚訝的發現,實際上隻有一個清單被觸發。這是因為第一個<code>userProvider.getUsers()</code> 中發生的錯誤将會終止整個鍊的上遊, <code>concat</code> 的第二個參數永遠不會被執行。

你看,RxJava 中的錯誤是非常具有破壞性的。它們被設計成緻命的信号來終止整條鍊的上遊。它們不應該是你的 Observable 接口的一部分。它們表現為意料之外的錯誤。

Observable 被設計成使用有效輸出來表示錯誤的觸發,這限制了它的使用範圍。複雜的鍊在錯誤的情況下如何工作很不明朗,是以很容易誤用這種 Observable 。這最終會導緻錯誤。非常惡心的錯誤,隻能偶爾重制的(特殊情況下,比如缺少網絡)而且不會留下堆棧痕迹的錯誤。

那麼,如何設計 Observable 來讓其傳回預期的錯誤呢?隻需讓它們傳回一些 <code>Result</code> 類,即包含操作的結果也包含異常,就像這樣:

将所有預期的異常包含進去,然後将所有不可預期的都放行而使程式崩潰。避免使用<code>onError</code> 消費者,讓 RxJava 為你控制崩潰。

現在,雖然這種途徑看起來不是特别優雅或直覺,并且産生了相當多的樣闆,但是我發現它會導緻最少的問題。此外,它看起來像是在 RxJava 中進行錯誤處理的『官方』方式。我看到過它在網際網路的多個讨論中被 RxJava 的維護者所推薦。

為了使你的 Retrofit Observable 傳回 <code>Result</code> 類,你可以使用這個友善的擴充功能:

這樣,你的 Observable <code>userProvider.getUsers()</code> 看起來可以像這樣:

<b>原文釋出時間為:2017年8月30日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>

繼續閱讀