使用rxjava全局处理网络异常进行改造

时间:2014-10-05 09:38:28

标签: android retrofit rx-java

我正在尝试在全局级别处理应用程序中的异常,因此改造会抛出一个错误,我会在某个特定的类中捕获它,并使用逻辑来处理这些错误。

我有一个界面

@POST("/token")
AuthToken refreshToken(@Field("grant_type") String grantType, @Field("refresh_token") String refreshToken);

和observables

/**
 * Refreshes auth token
 *
 * @param refreshToken
 * @return
 */
public Observable<AuthToken> refreshToken(String refreshToken) {
    return Observable.create((Subscriber<? super AuthToken> subscriber) -> {
        try {
            subscriber.onNext(apiManager.refreshToken(REFRESH_TOKEN, refreshToken));
            subscriber.onCompleted();
        } catch (Exception e) {
            subscriber.onError(e);
        }
    }).subscribeOn(Schedulers.io());
}

当我从服务器获得401(无效令牌或其他一些网络相关错误)时,我想刷新令牌并重复其余的呼叫。是否有一种方法可以使用rxjava对所有其余的调用执行此操作,并使用某种可观察的方法来全局捕获此错误,处理它并重复抛出它的调用?

现在我正在使用subject来捕获.subscribe()这样的错误

private static BehaviorSubject errorEvent = BehaviorSubject.create();

public static BehaviorSubject<RetrofitError> getErrorEvent() {
    return errorEvent;
}

并在一些电话中

getCurrentUser = userApi.getCurrentUser().observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                    (user) -> {
                        this.user = user;
                    },
                    errorEvent::onNext
            );

然后在我的主要活动中,我订阅了该行为主题并解析了错误

SomeApi.getErrorEvent().subscribe(
            (e) -> {
                //parse the error
            }
    );

但我无法重复调用抛出错误的observable。

2 个答案:

答案 0 :(得分:39)

您需要使用运算符onErrorResumeNext(Func1 resumeFunction),在official wiki

中有更好的解释
  

onErrorResumeNext()方法返回一个Observable,它反映了源Observable的行为,除非Observable在这种情况下调用onError(),而不是将该错误传播给Subscriber,onErrorResumeNext()而是开始镜像第二个备份Observable

在你的情况下,我会这样做:

getCurrentUser = userApi.getCurrentUser()
.onErrorResumeNext(refreshTokenAndRetry(userApi.getCurrentUser()))
.observeOn(AndroidSchedulers.mainThread())
            .subscribe(...)

其中:

    private <T> Func1<Throwable,? extends Observable<? extends T>> refreshTokenAndRetry(final Observable<T> toBeResumed) {
        return new Func1<Throwable, Observable<? extends T>>() {
            @Override
            public Observable<? extends T> call(Throwable throwable) {
                // Here check if the error thrown really is a 401
                if (isHttp401Error(throwable)) {
                    return refreshToken().flatMap(new Func1<AuthToken, Observable<? extends T>>() {
                        @Override
                        public Observable<? extends T> call(AuthToken token) {
                            return toBeResumed;
                        }
                    });
                }
                // re-throw this error because it's not recoverable from here
                return Observable.error(throwable);
            }
        };
    }

另请注意,此函数可以在其他情况下轻松使用,因为它不会使用已恢复的Observable发出的实际值进行输入。

答案 1 :(得分:1)

<div-tmp