RxJava 2 / Retrofit 2 - NetworkOnMainThreadException

时间:2017-05-16 17:05:29

标签: android retrofit rx-java retrofit2 rx-java2

我需要执行请求,如果我的令牌过期,我需要刷新它并重试请求。

我正在尝试这样做,目前我可以刷新令牌,但它会抛出一个NetworkOnMainThreadException。它完成了请求,更新令牌并到达日志,但是这个例外它杀了我。我怎么能避免这种情况?

    public Observable<Estabelecimento> listarEstabelecimentos() {
    return Observable.defer(this::getListarEstabelecimentoObservable)
            .retryWhen(throwableObservable -> throwableObservable.flatMap(
                    throwable -> {
                        if (throwable instanceof UnauthorizedException) {
                            return mRequestManager.getTokenObservable(AutoAtendimentoApplication.getContext())
                                    .doOnNext(response -> /* log stuff */)
                                    .flatMap((Function<AuthResponse, ObservableSource<?>>) response2 ->
                                            getListarEstabelecimentoObservable()
                                                    .doOnNext(estabelecimento ->
                                                            /* log stuff */)
                                                    )
                                    );
                        }
                        return Observable.error(throwable);
                    }));
}

NetWorkErrorHandler:

    public <T> T  processError(Response<T> response ) {
    switch (response.code()) {
        case 401:
            throw new UnauthorizedException();
        default:
            return response.body();
    }
}

令牌:

       private Observable<AuthResponse> getToken(Context context,
                                          @GrantType.GrantTypeDef String grantType, @Nullable String refreshToken) {

    SessionManager sessionManager = SessionManager.getInstance(context);
    Usuario usuario = sessionManager.getUser();

    AuthRequest request = new AuthRequest(usuario.getUsername(),
            usuario.getPassword(), grantType, refreshToken);

    return mAuthAPIService.getToken(request)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribeOn(Schedulers.io())
            .map(response -> storeTokens(response, context));
}

编辑1:我很确定问题发生在flatmap中并且放置subscribeOn并不能解决问题

编辑2:代码更新

3 个答案:

答案 0 :(得分:5)

您的网络正在主线程上运行,您可以通过将observeOn(AndroidSchedulers.mainThread())subscribeOn(Schedulers.io())添加到您的观察源来解决此问题。

observeOn(...)表示结果在指定的线程上发出 - 在这种情况下是UI线程。

subscribeOn(...)调用基本上是处理请求的地方。如果省略,则计算在当前线程上完成。

答案 1 :(得分:1)

好吧,让我试着用简单的英语解释一下。当您使用AndroidSchedulers.mainThread()时,它会将当前块包装在主线程中,它与您活动中的任何方法都没有区别。主要上下文不允许执行网络请求,因此您的失败原因。为了解决这个问题,你的可观察流需要改变以适应执行。

要测试这个理论,请将所有AndroidSchedulers.mainThread()更改为Schedulers.io(),因此您可以注释掉所有对ui处理的调用,例如Toast消息并留下简单的Log消息,试试看看是什么发生的情况。

你需要重构这一部分,因为它调用主线程。

 if (throwable instanceof UnauthorizedException) {
                            return mRequestManager.getTokenObservable(AutoAtendimentoApplication.getContext()).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io())
                                    .doOnNext(response -> Log.i("NEXT", "OK")).doOnError(throwable1 -> Log.i("ONERROR", "NOT OK"));

这是一个理论上的解决方案,不要将getToken包装在一个observable中,创建一个可以独立调用getToken的类,让它返回一个可以从中订阅和观察的observable。这些都是真的,目前你将上面的内容包装在主线程中。我不知道如何使这更简单                             }

答案 2 :(得分:1)

问题必须出在你的getToken上。您不需要observeOn和subscribeOn:

    return mAuthAPIService.getToken(request)
        .map(response -> storeTokens(response, context));

添加这些行会产生错误。我认为你需要在最终用户中指定它的唯一地方。