2

I am doing something like this: in my Fragment, I am observing a LiveData exposed by a ViewModel that encapsulates different UI states like error, loading, success state, etc. Within the said ViewModel, I start an RxJava2 stream that kicks off a chain of REST API requests via Retrofit:

    public LiveData<UIState> doSomething() {
        compositeDisposable.add(somethingRepo.doA()
            .andThen(somethingRepo.doB())
            .andThen(somethingRepo.doC())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .doOnSubscribe(disposable -> status.setValue(UIState.LOADING()))
            .subscribe(() -> status.setValue(UIState.SUCCESS()), error -> {
                Log.d("SettingsVM", "error: " + error.getClass().getSimpleName());

                if(error instanceof VersionAlreadyExistsException) {
                    status.setValue(UIState.VERSION_EXISTS());
                } else {
                    status.setValue(UIState.ERROR(error.getMessage()));
                }
            })
        );
        return status;
    }

However, according to my custom logger that implements HttpLoggingInterceptor.Logger, I am getting a java.net.SocketException: Socket closed during somethingRepo.doA(), but it seems that I am not getting the Exception in doSomething()'s onError. Here is the full somethingRepo.doA():

public Completable doA() {
    return api.doRequest(paramRepository.getUrl(false),
        paramRepository.getAuthorization(),
        paramRepository.getName())
        .flatMapCompletable(requestResponse-> {
            if(paramRepository.getVersion().contentEquals(requestResponse.getVersion())) {
                return Completable.error(new VersionAlreadyExistsException());
            } else {
                return Completable.complete();
            }
        });
}

api.doRequest is a GET request that looks like this:

@GET
Single<RequestResponse> doRequest (
        @Url String url,
        @Header("authorization") String authorization,
        @Query("merch") String name);

Interestingly, on another Android app, I sent a POST to the same URL I am using in the previous example and what I got was HTTP FAILED: java.net.ConnectException: Failed to connect to /180.232.98.122:7443.

I am aware of a similar issue being opened in the official Retrofit repo and a similar SO question here, but those two are unresolved, thus I would like to hear if anyone here has encountered the same behavior.

8
  • did you check internet connectivity ? Have declared internet permission ? Commented Jun 22, 2020 at 4:51
  • did you check apis on postman or any api client? Commented Jun 22, 2020 at 4:51
  • 1
    @Quicklearner I have the necessary permissions and a working internet connection. Also, sorry, the answer you linked to is not relevant to my question. Commented Jun 22, 2020 at 5:23
  • 1
    @Quicklearner Did you even read my question? If you really understand my question you would realize that my problem would not be resolved via a simple Postman check. Commented Jun 22, 2020 at 5:55
  • 2
    @Quicklearner Before any more irrelevant comments appear, please note that this error isn't caused by internet connectivity or lack of permissions. It is caused by this application closing the socket and then continuing to use it. It indicates a bug in the code somewhere. Commented Jun 22, 2020 at 7:45

1 Answer 1

1

I figured out what the problem was. Firstly, I implemented an "inactivity timer" that returns all screens (I use Fragments for screens) to the first/home screen in my app. So let's say a value of 30 seconds was set to the timer, if no interaction with the UI happens and the timer reaches 30 secs then my app goes back to the home screen.

Secondly, the ViewModel where I kick off the requests is tied to a Fragment since it implements DefaultLifecycleObserver, which means that it will receive LifecycleOwner changes/callbacks, provided I call this: getViewLifecycleOwner().getLifecycle().addObserver(viewModel) in my Fragment#onViewCreated. I then override onStop(@NonNull LifecycleOwner owner) in the ViewModel and call compositeDisposable.clear(), which means I want to sort of cancel all pending API request I made using RxJava2 once the Fragment my ViewModel is tied to receives onStop.

The final piece of info is that the timeout value for my OkHttp client is greater than the inactivity timer value, so the inactivity timer abruptly causes my request API to be cancelled even before it finishes.

To summarize, here is what happens: when the inactivity timer is up, the request API Fragment's onStop is called as the screen changes back to the home screen, and then the ViewModel calls compositeDisposable.clear() which disposes the current RxJava disposable, thus I am unable to receive anything on onError.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.