当前位置: 首页 > 知识库问答 >
问题:

在RxJava中处理API异常

祁俊拔
2023-03-14

我目前正试图把精力放在RxJava上,但我在以优雅的方式处理服务调用异常方面遇到了一点麻烦。

基本上,我有一个(改型)服务,它返回一个可观察的 ServiceResponse的定义如下:

public class ServiceResponse {
    private int status;
    private String message;
    private JsonElement data;

    public JsonElement getData() {
        return data;
    }

    public int getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }
}

现在,我想要的是将该泛型响应映射到data JsonElement字段中包含的列表 (我假设您不关心account对象的外观,所以我不会用它污染文章)。下面的代码对于success案例非常有效,但是我找不到一个很好的方法来处理我的API异常:

service.getAccounts()
       .subscribeOn(Schedulers.io())
       .observeOn(AndroidSchedulers.mainThread())
       .map(new Func1<ServiceResponse, AccountData>() {
               @Override
               public AccountData call(ServiceResponse serviceResponse) {

                   // TODO: ick. fix this. there must be a better way...
                   ResponseTypes responseType = ResponseTypes.from(serviceResponse.getStatus());
                   switch (responseType) {
                       case SUCCESS:
                           Gson gson = new GsonBuilder().create();
                           return gson.fromJson(serviceResponse.getData(), AccountData.class);
                       case HOST_UNAVAILABLE:
                           throw new HostUnavailableException(serviceResponse.getMessage());
                       case SUSPENDED_USER:
                           throw new SuspendedUserException(serviceResponse.getMessage());
                       case SYSTEM_ERROR:
                       case UNKNOWN:
                       default:
                           throw new SystemErrorException(serviceResponse.getMessage());
                   }
              }
        })
        .map(new Func1<AccountData, List<Account>>() {
                @Override
                public List<Account> call(AccountData accountData) {
                    Gson gson = new GsonBuilder().create();
                    List<Account> res = new ArrayList<Account>();
                    for (JsonElement account : accountData.getAccounts()) {
                        res.add(gson.fromJson(account, Account.class));
                    }
                    return res;
                }
        })
        .subscribe(accountsRequest);

有没有更好的办法做到这一点?这是有效的,onError将触发我的观察者,我将收到我抛出的错误,但这绝对不是我做的正确的。

提前道谢!

编辑:

我希望有一个可以从UI调用的类(例如,一个活动,或片段,或其他)。该类将使用观察者 <list > 作为参数,如下所示:

public Subscription loadAccounts(Observer<List<Account>> observer, boolean forceRefresh) {
    ...
}

该方法将返回一个订阅,当UI被分离/销毁/等等时,该订阅可以被取消。

参数化的观察者将处理通过帐户列表的成功响应的onNext。OnError将处理任何异常,但也会传递任何API异常(例如,如果响应状态!=200,我们将创建一个Throwable并将其传递给OnError)。理想情况下,我不想只是“抛出”异常,我想直接将它传递给观察者。我看到的所有例子都是这样做的。

@Singleton
public class AccountsDatabase {

    private AccountsService service;

    private List<Account> accountsCache = null;
    private PublishSubject<ServiceResponse> accountsRequest = null;

    @Inject
    public AccountsDatabase(AccountsService service) {
        this.service = service;
    }

    public Subscription loadAccounts(Observer<List<Account>> observer, boolean forceRefresh) {

        ObserverWrapper observerWrapper = new ObserverWrapper(observer);

        if (accountsCache != null) {
            // We have a cached value. Emit it immediately.
            observer.onNext(accountsCache);
        }

        if (accountsRequest != null) {
            // There's an in-flight network request for this section already. Join it.
            return accountsRequest.subscribe(observerWrapper);
        }

        if (accountsCache != null && !forceRefresh) {
            // We had a cached value and don't want to force a refresh on the data. Just
            // return an empty subscription
            observer.onCompleted();
            return Subscriptions.empty();
        }

        accountsRequest = PublishSubject.create();

        accountsRequest.subscribe(new ObserverWrapper(new EndObserver<List<Account>>() {

            @Override
            public void onNext(List<Account> accounts) {
                accountsCache = accounts;
            }

            @Override
            public void onEnd() {
                accountsRequest = null;
            }
        }));

        Subscription subscription = accountsRequest.subscribe(observerWrapper);

        service.getAccounts()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(accountsRequest);

        return subscription;
    }

    static class ObserverWrapper implements Observer<ServiceResponse> {

        private Observer<List<Account>> observer;

        public ObserverWrapper(Observer<List<Account>> observer) {
            this.observer = observer;
        }

        @Override
        public void onCompleted() {
            observer.onCompleted();
        }

        @Override
        public void onError(Throwable e) {
            observer.onError(e);
        }

        @Override
        public void onNext(ServiceResponse serviceResponse) {
            ResponseTypes responseType = ResponseTypes.from(serviceResponse.getStatus());
            switch (responseType) {
                case SUCCESS:
                    Gson gson = new GsonBuilder().create();
                    AccountData accountData = gson.fromJson(serviceResponse.getData(), AccountData.class);
                    List<Account> res = new ArrayList<>();
                    for (JsonElement account : accountData.getAccounts()) {
                        res.add(gson.fromJson(account, Account.class));
                    }
                    observer.onNext(res);
                    observer.onCompleted();
                    break;
                default:
                    observer.onError(new ApiException(serviceResponse.getMessage(), responseType));
                    break;
            }
        }
    }
}

共有1个答案

袁鸿畅
2023-03-14

请阅读下面我添加了一个编辑。

使用RXJava在action/func/observer中抛出是完全正确的。该异常将由框架直接传播给您的观察者。如果您仅限于调用onError,那么您就会扭曲自己来实现这一点。

话虽如此,一个建议是简单地移除这个包装,并在service.getAccount中添加一个简单的验证操作...观测链。

以下是使用我的建议简化的loadAccount方法。

public Subscription loadAccounts(Observer<List<Account>> observer, boolean forceRefresh) {
    if (accountsCache != null) {
        // We have a cached value. Emit it immediately.
        observer.onNext(accountsCache);
    }

    if (accountsRequest != null) {
        // There's an in-flight network request for this section already. Join it.
        return accountsRequest.subscribe(observer);
    }

    if (accountsCache != null && !forceRefresh) {
        // We had a cached value and don't want to force a refresh on the data. Just
        // return an empty subscription
        observer.onCompleted();
        return Subscriptions.empty();
    }

    accountsRequest = PublishSubject.create();
    accountsRequest.subscribe(new EndObserver<List<Account>>() {

        @Override
        public void onNext(List<Account> accounts) {
            accountsCache = accounts;
        }

        @Override
        public void onEnd() {
            accountsRequest = null;
        }
    });

    Subscription subscription = accountsRequest.subscribe(observer);

    service.getAccounts()
            .doOnNext(new ValidateServiceResponseOrThrow())
            .map(new MapValidResponseToAccountList())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(accountsRequest);

    return subscription;
}

private static class ValidateResponseOrThrow implements Action1<ServiceResponse> {
        @Override
        public void call(ServiceResponse response) {
            ResponseTypes responseType = ResponseTypes.from(serviceResponse.getStatus());
            if (responseType != SUCCESS)
                throw new ApiException(serviceResponse.getMessage(), responseType));
        }
    }

private static class MapValidResponseToAccountList implements Func1<ServiceResponse, List<Account>> {
    @Override
    public Message call(ServiceResponse response) {
        // add code here to map the ServiceResponse into the List<Accounts> as you've provided already
    }
}

编辑:除非有人另有说法,否则我认为使用FlatMap返回错误是最好的做法。我在过去曾抛出过Action的异常,但我不认为这是推荐的方法。

如果使用FlatMap,将有一个更清晰的异常堆栈。如果从操作内部抛出,异常堆栈实际上将包含rx.exceptions.OnErrorThrowable$OnNextValue异常,这并不理想。

让我使用flatMap来演示上面的示例。

private static class ValidateServiceResponse implements rx.functions.Func1<ServiceResponse, Observable<ServiceResponse>> {
    @Override
    public Observable<ServiceResponse> call(ServiceResponse response) {
        ResponseTypes responseType = ResponseTypes.from(serviceResponse.getStatus());
        if (responseType != SUCCESS)
            return Observable.error(new ApiException(serviceResponse.getMessage(), responseType));
        return Observable.just(response);
    }
}

service.getAccounts()
    .flatMap(new ValidateServiceResponse())
    .map(new MapValidResponseToAccountList())
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(accountsRequest);

正如你所看到的,差别是细微的。ValidateServiceResponse现在实现了Func1而不是Action1并且我们不再使用Throw关键字。我们改用observable.error(new Throwable)。我相信这更符合预期的Rx合同。

 类似资料:
  • 我正在使用WebAPI2.0创建我自己的项目。我的Api包含添加、获取和预订产品的功能。 我想处理异常,但有一些问题我有点困惑。 我有一个控制器:与动作方法。 现在,如果产品已经存在,那么数据访问层将抛出 我想处理这个问题,并像这样回复: 返回HTTP状态代码200是否合理 如果不向控制器添加,我怎么做,因为我认为这不是正确的方法 谢谢

  • 考虑以下示例: 这将输出从1到5的数字,然后打印异常。 我想要实现的是使观察器保持订阅状态,并在抛出异常后继续运行,即打印从1到10的所有数字。 我尝试过使用和其他各种错误处理操作符,但正如文档中所述,它们的目的是处理可观察对象本身发出的错误。 最直接的解决方案是将的整个主体包装成一个try-catch块,但对我来说这听起来不是一个好的解决方案。在类似的Rx中。NET问题,提出的解决方案是制作一个

  • 我主要是为技术精明的人编写一个小工具,例如程序员、工程师等,因为这些工具通常是快速的,随着时间的推移,我知道会有未处理的异常,用户不会介意。我希望用户能够向我发送回溯,这样我就可以检查发生了什么,并可能改进应用程序。 我通常做wxPython编程,但我最近做了一些Java。我已经将

  • 我正在做我的一项任务。这相当直截了当。包含单个输入的HTML表单被提交到Servlet,Servlet获取参数,基于参数创建消息,将消息作为属性添加到请求,并使用requestdispatcher转发到jsp以显示消息。 我有一个要求,如果参数丢失,我需要显示一个错误页面。问题是我不能显式地检查null,也不能使用try/catch块。我的猜测是,目标是在web中定义错误页面。xml页面来处理特定

  • 我遇到的问题是,当我的中出现异常时,没有处理异常。我希望它能在那里被捕获,就像它使用旧的ASP.NET Web API一样。 那么,如何捕获所有应用程序异常以及操作筛选器中的任何异常呢?

  • 问题内容: 考虑以下示例: 这将输出从1到5的数字,然后打印异常。 我要实现的是使观察者保持订阅状态,并在引发异常后继续运行,即打印从1到10的所有数字。 我曾尝试使用和其他各种错误处理运算符,但是,正如文档中所述,它们的目的是处理可观察对象自身发出的错误。 最直接的解决方案是将整个过程包装到try- catch块中,但这对我来说似乎不是一个好的解决方案。在类似的Rx.NET问题中,提出的解决方案