业务异常 java_谈谈RxJava处理业务异常的几种方式
此文介紹了RxJava處理業(yè)務(wù)異常的幾種方式,分享給大伙。具體如下:
關(guān)于異常
Java的異??梢苑譃閮煞N:運(yùn)行時異常和檢查性異常。
運(yùn)行時異常:
RuntimeException類及其子類都被稱為運(yùn)行時異常,這種異常的特點(diǎn)是Java編譯器不去檢查它,也就是說,當(dāng)程序中可能出現(xiàn)這類異常時,即使沒有用try...catch語句捕獲它,也沒有用throws字句聲明拋出它,還是會編譯通過。
檢查性異常:
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬于檢查性異常。檢查性異常必須被顯式地捕獲或者傳遞。當(dāng)程序中可能出現(xiàn)檢查性異常時,要么使用try-catch語句進(jìn)行捕獲,要么用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)于多給對方一次機(jī)會。
在這里,我們使用retryWhen操作符,它將錯誤傳遞給另一個被觀察者來決定是否要重新給訂閱這個被觀察者。
聽上去有點(diǎn)拗口,直接上代碼吧。
/**
* 獲取內(nèi)容
* @param fragment
* @param param
* @param cacheKey
* @return
*/
public Maybe 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).toLifecycleTransformer())
.compose(RxUtils.toCacheTransformer(cacheKey));
}
這個例子是一個網(wǎng)絡(luò)請求,compose的內(nèi)容可以忽略。如果網(wǎng)絡(luò)請求失敗的話,會調(diào)用retryWhen操作符。RetryWithDelay實(shí)現(xiàn)了Function接口,RetryWithDelay是一個重試的機(jī)制,包含了重試的次數(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;
/**
* 重試機(jī)制
* Created by tony on 2017/11/6.
*/
public class RetryWithDelay implements Function, 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>() {
@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);
}
}
});
}
}
如果運(yùn)氣好重試成功了,那用戶在無感知的情況下可以繼續(xù)使用產(chǎn)品。如果多次重試都失敗了,那么必須在onError時做一些異常的處理,提示用戶可能是網(wǎng)絡(luò)的原因了。
2. 返回一個默認(rèn)值
有時出錯只需返回一個默認(rèn)值,有點(diǎn)類似Java 8 Optional的orElse()
RetrofitManager.get()
.adService()
.vmw(param)
.compose(RxLifecycle.bind(fragment).toLifecycleTransformer())
.subscribeOn(Schedulers.io())
.onErrorReturn(new Function() {
@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操作符,讓請求并行處理,等所有的請求完了之后再進(jìn)行合并操作。某些請求失敗的話,我使用了重試機(jī)制,某些請求失敗的話我給了默認(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.NetworkErrorException
import android.content.Context
import com.safframework.log.L
import io.reactivex.observers.DisposableMaybeObserver
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
/**
* Created by Tony Shen on 2017/8/8.
*/
abstract class BaseMaybeObserver : DisposableMaybeObserver() {
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.NetworkErrorException
import android.content.Context
import com.safframework.log.L
import io.reactivex.observers.ResourceCompletableObserver
import java.net.ConnectException
import java.net.SocketTimeoutException
import 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的目的是因?yàn)榇a更加簡潔,避免使用switch或者各種if(XX instancof xxException)來判斷異常類型,可以跟Java代碼無縫結(jié)合。
下面的代碼展示了如何使用BaseMaybeObserver,即使遇到異常BaseMaybeObserver的onError也會做相應(yīng)地處理。如果有特殊的需求,也可以重寫onError方法。
model.getContent(VideoFragment.this,param, cacheKey)
.compose(RxJavaUtils.maybeToMain())
.doFinally(new Action() {
@Override
public void run() throws Exception {
refreshlayout.finishRefresh();
}
})
.subscribe(new BaseMaybeObserver(){
@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ā)邏輯為:
自定義異常->網(wǎng)絡(luò)異常->服務(wù)器異常->內(nèi)部程序異常->未知異常
除了以上自定義異常之外,此庫包含其它異常分發(fā),默認(rèn)適應(yīng)場景為:Rx+Json
自定義異常使用請調(diào)用,ExceptionParseMgr類的addCustomerParser方法添加業(yè)務(wù)異常
這個庫對原先的代碼無侵入性。此外,他還提供了另一種思路,結(jié)合compose來處理一些特定的業(yè)務(wù)異常。
總結(jié)
此文僅僅是總結(jié)了個人使用RxJava遇到業(yè)務(wù)異常的情況,并對此做了一些相應(yīng)地處理,肯定是不能覆蓋開發(fā)的方方面面,僅作為拋磚引玉,如果有更好、更優(yōu)雅的處理方式,一定請告知。
上面即是這篇文章的內(nèi)容,希望對各位的學(xué)習(xí)有所啟發(fā),也希望大家多多支持學(xué)貓?jiān)诰€(shtml.net)。
本文來源:http://www.jianshu.com/p/423cc558556b
總結(jié)
以上是生活随笔為你收集整理的业务异常 java_谈谈RxJava处理业务异常的几种方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java的基础类型和字节大小_java的
- 下一篇: java路径怎么找_Java路径怎么找