Felipe Castilhos
Felipe Castilhos

Reputation: 826

RxJava 2 / Retrofit 2 - NetworkOnMainThreadException

I need to perform a request, if my token is expired I need to refresh it and retry the request.

This is how I'm trying to do it, at the moment I can refresh the token but it throws me a NetworkOnMainThreadException. It finishes the request,update the token and reaches logs, but that exception its killing me. How can I avoid that?

    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();
    }
}

Token:

       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));
}

Edit 1: I'm pretty sure the problem happens inside the flatmap and put subscribeOn does not solve the problem

Edit 2: Code update

Upvotes: 8

Views: 5655

Answers (3)

Jean Robert
Jean Robert

Reputation: 759

the problem must be on your getToken. You do not need the observeOn and the subscribeOn:

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

Adding those lines genererate the error. I do think the only place you need to specify its in the final subscriber.

Upvotes: 1

Remario
Remario

Reputation: 3863

Alright let me try and explain this in plain English. When you use AndroidSchedulers.mainThread(), it wraps the current block in the main thread, its no different from any method in your activity. The main context does not allow execution of networks requests, hence why yours fail. To remedy this your flow of observable need to change to accommodate the execution.

To test this theory, change all AndroidSchedulers.mainThread() to Schedulers.io() , therefore you would of to comment out all call to ui processing , like toast messages and leave behind simple Log messages , try it and see what happens.

you need to refactor this section, because it calls on the main thread.

 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"));

Here is a theoretical solution, do not wrap the getToken in a observable, create a class where the getToken can be called independently , let it return an observable from which you can then subscribe and observe from. thats all really, currently you wrap the above in the main thread.I do not know how else to make this simpler }

Upvotes: 1

user_4685247
user_4685247

Reputation: 2995

Your network is running on main thread, you can solve it by adding observeOn(AndroidSchedulers.mainThread()) and subscribeOn(Schedulers.io()) to your observable.

The observeOn(...) means that the results are emmited on the specified thread - in this case the UI thread.

The subscribeOn(...) call is basically where the request is processed. If omitted the computation is done on current thread.

Upvotes: 10

Related Questions