隨著互聯(lián)網(wǎng)的快速發(fā)展,很多程序的操作方法已經(jīng)改朝換代,今天武林技術(shù)頻道小編為大家整理了淺談RxJava處理業(yè)務(wù)異常的幾種方式,希望對你學(xué)習(xí)這方面知識有所幫助。
關(guān)于異常
Java的異常可以分為兩種:運行時異常和檢查性異常。
運行時異常:
RuntimeException類及其子類都被稱為運行時異常,這種異常的特點是Java編譯器不去檢查它,也就是說,當(dāng)程序中可能出現(xiàn)這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過。
檢查性異常:
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬于檢查性異常。檢查性異常必須被顯式地捕獲或者傳遞。當(dāng)程序中可能出現(xiàn)檢查性異常時,要么使用try-catch語句進行捕獲,要么用throws子句拋出,否則編譯無法通過。
處理業(yè)務(wù)異常
業(yè)務(wù)異常:
指的是正常的業(yè)務(wù)處理時,由于某些業(yè)務(wù)的特殊要求而導(dǎo)致處理不能繼續(xù)所拋出的異常。在業(yè)務(wù)層或者業(yè)務(wù)的處理方法中拋出異常,在表現(xiàn)層中攔截異常,以友好的方式反饋給使用者,以便其可以依據(jù)提示信息正確的完成任務(wù)功能的處理。
1. 重試
不是所有的錯誤都需要立馬反饋給用戶,比如說在弱網(wǎng)絡(luò)環(huán)境下調(diào)用某個接口出現(xiàn)了超時的現(xiàn)象,也許再請求一次接口就能獲得數(shù)據(jù)。那么重試就相當(dāng)于多給對方一次機會。
在這里,我們使用retryWhen操作符,它將錯誤傳遞給另一個被觀察者來決定是否要重新給訂閱這個被觀察者。
聽上去有點拗口,直接上代碼吧。
/** * 獲取內(nèi)容 * @param fragment * @param param * @param cacheKey * @return */ public Maybe<ContentModel> getContent(Fragment fragment, ContentParam param, String cacheKey) { if (apiService == null) { apiService = RetrofitManager.get().apiService(); } return apiService.loadContent(param) .retryWhen(new RetryWithDelay(3,1000)) .compose(RxLifecycle.bind(fragment).<ContentModel>toLifecycleTransformer()) .compose(RxUtils.<ContentModel>toCacheTransformer(cacheKey)); }這個例子是一個網(wǎng)絡(luò)請求,compose的內(nèi)容可以忽略。如果網(wǎng)絡(luò)請求失敗的話,會調(diào)用retryWhen操作符。RetryWithDelay實現(xiàn)了Function接口,RetryWithDelay是一個重試的機制,包含了重試的次數(shù)和重試時間隔的時間。
import com.safframework.log.L;import org.reactivestreams.Publisher;import java.util.concurrent.TimeUnit;import io.reactivex.Flowable;import io.reactivex.annotations.NonNull;import io.reactivex.functions.Function;/** * 重試機制 * Created by tony on 2017/11/6. */public class RetryWithDelay implements Function<Flowable<? extends Throwable>, Publisher<?>> { private final int maxRetries; private final int retryDelayMillis; private int retryCount; public RetryWithDelay(final int maxRetries, final int retryDelayMillis) { this.maxRetries = maxRetries; this.retryDelayMillis = retryDelayMillis; this.retryCount = 0; } @Override public Publisher<?> apply(@NonNull Flowable<? extends Throwable> attempts) throws Exception { return attempts.flatMap(new Function<Throwable, Publisher<?>>() { @Override public Publisher<?> apply(Throwable throwable) throws Exception { if (++retryCount <= maxRetries) { L.i("RetryWithDelay", "get error, it will try after " + retryDelayMillis + " millisecond, retry count " + retryCount); // When this Observable calls onNext, the original // Observable will be retried (i.e. re-subscribed). return Flowable.timer(retryDelayMillis, TimeUnit.MILLISECONDS); } else { // Max retries hit. Just pass the error along. return Flowable.error(throwable); } } }); }}如果運氣好重試成功了,那用戶在無感知的情況下可以繼續(xù)使用產(chǎn)品。如果多次重試都失敗了,那么必須在onError時做一些異常的處理,提示用戶可能是網(wǎng)絡(luò)的原因了。
2. 返回一個默認(rèn)值
有時出錯只需返回一個默認(rèn)值,有點類似Java 8 Optional的orElse()
RetrofitManager.get() .adService() .vmw(param) .compose(RxLifecycle.bind(fragment).<VMWModel>toLifecycleTransformer()) .subscribeOn(Schedulers.io()) .onErrorReturn(new Function<Throwable, VMWModel>() { @Override public VMWModel apply(Throwable throwable) throws Exception { return new VMWModel(); } });上面的例子使用了onErrorReturn操作符,表示當(dāng)發(fā)生錯誤的時候,發(fā)射一個默認(rèn)值然后結(jié)束數(shù)據(jù)流。所以 Subscriber 看不到異常信息,看到的是正常的數(shù)據(jù)流結(jié)束狀態(tài)。
跟它類似的還有onErrorResumeNext操作符,表示當(dāng)錯誤發(fā)生的時候,使用另外一個數(shù)據(jù)流繼續(xù)發(fā)射數(shù)據(jù)。在返回的被觀察者中是看不到錯誤信息的。
使用了onErrorReturn之后,onError是不是就不做處理了?onErrorReturn的確是返回了一個默認(rèn)值,如果onErrorReturn之后還有類似doOnNext的操作,并且doOnNext中出錯的話,onError還是會起作用的。
曾經(jīng)遇到過一個復(fù)雜的業(yè)務(wù)場景,需要多個網(wǎng)絡(luò)請求合并結(jié)果。這時,我使用zip操作符,讓請求并行處理,等所有的請求完了之后再進行合并操作。某些請求失敗的話,我使用了重試機制,某些請求失敗的話我給了默認(rèn)值。
3. 使用onError處理異常
現(xiàn)在的Android開發(fā)中,網(wǎng)絡(luò)框架是Retrofit的天下。在接口定義的返回類型中,我一般喜歡用Maybe、Completable來代替Observable。
我們知道RxJava在使用時,觀察者會調(diào)用onNext、onError、onComplete方法,其中onError方法是事件在傳遞或者處理的過程中發(fā)生錯誤后會調(diào)用到。
下面的代碼,分別封裝兩個基類的Observer,都重寫了onError方法用于處理各種網(wǎng)絡(luò)異常。這兩個基類的Observer是在使用Retrofit時使用的。
封裝一個BaseMaybeObserver
import android.accounts.NetworkErrorExceptionimport android.content.Contextimport com.safframework.log.Limport io.reactivex.observers.DisposableMaybeObserverimport java.net.ConnectExceptionimport java.net.SocketTimeoutExceptionimport java.net.UnknownHostException/** * Created by Tony Shen on 2017/8/8. */abstract class BaseMaybeObserver<T> : DisposableMaybeObserver<T>() { internal var mAppContext: Context init { mAppContext = AppUtils.getApplicationContext() } override fun onSuccess(data: T) { onMaybeSuccess(data) } abstract fun onMaybeSuccess(data: T) override fun onError(e: Throwable) { var message = e.message L.e(message) when(e) { is ConnectException -> message = mAppContext.getString(R.string.connect_exception_error) is SocketTimeoutException -> message = mAppContext.getString(R.string.timeout_error) is UnknownHostException -> message = mAppContext.getString(R.string.network_error) is NetworkErrorException -> message = mAppContext.getString(R.string.network_error) else -> message = mAppContext.getString(R.string.something_went_wrong) } RxBus.get().post(FailedEvent(message)) } override fun onComplete() {}}封裝一個BaseCompletableObserver
import android.accounts.NetworkErrorExceptionimport android.content.Contextimport com.safframework.log.Limport io.reactivex.observers.ResourceCompletableObserverimport java.net.ConnectExceptionimport java.net.SocketTimeoutExceptionimport java.net.UnknownHostException/** * Created by Tony Shen on 2017/8/8. */abstract class BaseCompletableObserver : ResourceCompletableObserver() { internal var mAppContext: Context init { mAppContext = AppUtils.getApplicationContext() } override fun onComplete() { onSuccess() } abstract fun onSuccess() override fun onError(e: Throwable) { var message = e.message L.e(message) when(e) { is ConnectException -> message = mAppContext.getString(R.string.connect_exception_error) is SocketTimeoutException -> message = mAppContext.getString(R.string.timeout_error) is UnknownHostException -> message = mAppContext.getString(R.string.network_error) is NetworkErrorException -> message = mAppContext.getString(R.string.network_error) else -> message = mAppContext.getString(R.string.something_went_wrong) } RxBus.get().post(FailedEvent(message)) }}在這里用到了Kotlin來寫這兩個基類,使用Kotlin的目的是因為代碼更加簡潔,避免使用switch或者各種if(XX instancof xxException)來判斷異常類型,可以跟Java代碼無縫結(jié)合。
下面的代碼展示了如何使用BaseMaybeObserver,即使遇到異常BaseMaybeObserver的onError也會做相應(yīng)地處理。如果有特殊的需求,也可以重寫onError方法。
model.getContent(VideoFragment.this,param, cacheKey) .compose(RxJavaUtils.<ContentModel>maybeToMain()) .doFinally(new Action() { @Override public void run() throws Exception { refreshlayout.finishRefresh(); } }) .subscribe(new BaseMaybeObserver<ContentModel>(){ @Override public void onMaybeSuccess(ContentModel data) { adpter.addDataToFront(data); } });4. 內(nèi)部異常使用責(zé)任鏈模式來分發(fā)
這是微信中一位網(wǎng)友提供的方法,他做了一個很有意思的用于異常分發(fā)的一個庫,github地址:https://github.com/vihuela/Retrofitplus
內(nèi)部異常使用責(zé)任鏈分發(fā),分發(fā)邏輯為:
這個庫對原先的代碼無侵入性。此外,他還提供了另一種思路,結(jié)合compose來處理一些特定的業(yè)務(wù)異常。
本文是武林技術(shù)頻道小編為大家?guī)淼臏\談RxJava處理業(yè)務(wù)異常的幾種方式,如果有更好快捷的方法,請告訴小編,讓我們一起學(xué)習(xí)一起進步吧!
新聞熱點
疑難解答
圖片精選