【问题标题】:RxJava 2 / Retrofit 2 - NetworkOnMainThreadExceptionRxJava 2 / 改造 2 - NetworkOnMainThreadException
【发布时间】:2017-10-15 21:30:15
【问题描述】:

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

这就是我正在尝试的方式,目前我可以刷新令牌,但它会抛出 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);
                    }));
}

网络错误处理程序:

    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:我很确定问题发生在平面地图内部,并且 subscribeOn 不能解决问题

编辑 2:代码更新

【问题讨论】:

  • 我猜这行 mRequestManager.getTokenObservable(AtendimentoApplication.getContext()) .doOnNext(response -> Log.i("NEXT", "OK")).doOnError(throwable1 -> Log .i("ONERROR", "NOT OK"));导致错误?
  • 发布你的 mRequestManager.getTokenObservable 实现
  • 这一行,estabelecimento.getIdSincronizacao() 发出请求?
  • mLoginViewModel.listEstabelecimentos() 这是请求。你向我展示的那一行解析数据
  • 提供更多代码!

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


【解决方案1】:

您的网络在主线程上运行,您可以通过将 observeOn(AndroidSchedulers.mainThread())subscribeOn(Schedulers.io()) 添加到您的 observable 来解决它。

observeOn(...) 表示将结果发送到指定线程 - 在本例中为 UI 线程。

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

【讨论】:

  • 你认为我应该把它放在哪里?已在订阅者处完成
  • 基本上在您发出网络请求的任何地方,如果您不需要将结果放在 UI 线程上,则不需要 observeOn(...)
  • 添加到我执行订阅问题的地方
  • 我想你也需要把它添加到getTokenObservable
  • Schedulers.io() 就是这样做的,不需要创建不需要的 Thread 对象。
【解决方案2】:

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

要验证这个理论,请将所有 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,然后您可以从中订阅和观察。就是这样,目前您将上述内容包装在主线程中。我不知道如何使这更简单 }

【讨论】:

  • 将所有AndroidSchedulers.mainThread() 更改为Schedulers.io() 并不能解决问题,仍然在主线程异常上获取网络。
【解决方案3】:

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

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

添加这些行会产生错误。我确实认为您需要在最终订阅者中指定它的唯一位置。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多