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

ddo*_*dog 19 android rx-java retrofit

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

我有一个界面

@POST("/token")
AuthToken refreshToken(@Field("grant_type") String grantType, @Field("refresh_token") String refreshToken);
Run Code Online (Sandbox Code Playgroud)

和可观察的

/**
 * 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());
}
Run Code Online (Sandbox Code Playgroud)

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

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

private static BehaviorSubject errorEvent = BehaviorSubject.create();

public static BehaviorSubject<RetrofitError> getErrorEvent() {
    return errorEvent;
}
Run Code Online (Sandbox Code Playgroud)

并在一些电话中

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

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

SomeApi.getErrorEvent().subscribe(
            (e) -> {
                //parse the error
            }
    );
Run Code Online (Sandbox Code Playgroud)

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

a.b*_*cci 39

您需要使用运算符onErrorResumeNext(Func1 resumeFunction),在官方维基中更好地解释:

所述onErrorResumeNext()方法返回可观察到的,反映了源的行为观察的,除非该可观察所调用的onError()在这种情况下,而不是传播的错误的订户,onErrorResumeNext()将代替开始镜像的第二,备份可观察

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

getCurrentUser = userApi.getCurrentUser()
.onErrorResumeNext(refreshTokenAndRetry(userApi.getCurrentUser()))
.observeOn(AndroidSchedulers.mainThread())
            .subscribe(...)
Run Code Online (Sandbox Code Playgroud)

哪里:

    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);
            }
        };
    }
Run Code Online (Sandbox Code Playgroud)

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

  • 添加到上面的答案中,我将使用`defer()`运算符来避免使用旧的observable重试请求.代码:`onErrorResumeNext(refreshTokenAndRetry(Observable.defer(() - > userApi.getCurrentUser())))`.希望对某人有用:) (4认同)