Retrofit: Random throw java.io.InterruptedIOException when use OkHttpClient on Android

Created on 30 Jun 2015  路  22Comments  路  Source: square/retrofit

I have set up a RestAdpter with OKHttpClient:

RestAdapter.Builder builder = new RestAdapter.Builder()
                .setClient(new OkClient(okHttpClient))
//                .setClient(new AndroidApacheClient())
                .setConverter(converter)
                .setEndpoint(API_ENDPOINT);

When I tried to make some calls, sometimes Retrofit throw a java.io.InterruptedIOException:

java.io.InterruptedIOException: thread interrupted
            at okio.Timeout.throwIfReached(Timeout.java:145)
            at okio.Okio$1.write(Okio.java:75)
            at okio.AsyncTimeout$1.write(AsyncTimeout.java:155)
            at okio.RealBufferedSink.flush(RealBufferedSink.java:221)
            at com.squareup.okhttp.internal.http.HttpConnection.flush(HttpConnection.java:141)
            at com.squareup.okhttp.internal.http.HttpTransport.finishRequest(HttpTransport.java:52)
            at com.squareup.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:915)
            at com.squareup.okhttp.internal.http.HttpEngine.access$300(HttpEngine.java:95)
            at com.squareup.okhttp.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:902)
            at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:760)
            at com.squareup.okhttp.Call.getResponse(Call.java:274)
            at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:230)
            at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:201)
            at com.squareup.okhttp.Call.execute(Call.java:81)
            at retrofit.client.OkClient.execute(OkClient.java:53)
            at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:326)
            at retrofit.RestAdapter$RestHandler.access$100(RestAdapter.java:220)
            at retrofit.RestAdapter$RestHandler$1.invoke(RestAdapter.java:265)
            at retrofit.RxSupport$2.run(RxSupport.java:55)
            at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:442)
            at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
            at java.util.concurrent.FutureTask.run(FutureTask.java:137)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
            at retrofit.Platform$Android$2$1.run(Platform.java:142)
            at java.lang.Thread.run(Thread.java:856)

I used Observable to subscribe for the result but no Error is thrown back to subscriber.
And It doesn't happen when I use AndroidApacheClient.

Most helpful comment

As I understand now, RxJava for some reason interrupts thread on unsubscription. So if you unsubscribe during request execution, errors like this can appear.

All 22 comments

This is a write timeout which defaults to 15 seconds. I have no idea what Apache's defaults to, but it's probably higher, or infinite. You can call setWriteTimeout on the okHttpClient instance to set this to something larger.

(It's also not a Retrofit problem but an HTTP client configuration issue)

Thanks for your quick reply. I have used Retrofit a long time before, but it's first time I encountered this issue.

@JakeWharton, I'm also faced with the same problem. When I'm trying to compose three requests with retrofit I get java.io.InterruptedIOException.
Here is call method:
Observable.zip(mRouteClient.getRoutes(token), mRouteClient.getStops(token), mRouteClient.getTransportTypes(token), (this::saveData)).subscribe(new SimpleObserver<Void>() {

And it works correctly if I'm composing only two requests.

I finally figured it out. Problem was in Gson parsing. Gson couldn't serialize my POJO.

I have exactly the same problem, but only when I run tests with Robolectric. I have one test that performs a request, it works ok. Then a second test runs, this performs the same request again, but then it fails throwing that error. If I comment out first request, then it doesn't fail. Any idea where the error is coming from? I use rxjava, so I am returning Obervables with the response on those requests.

Thanks.

Fixed by Okio square/okio#164

Probably this is a connected issue: https://github.com/square/retrofit/issues/844

Not fixed. I have it with okhttp:2.7.0 and retrofit:1.9.0, Google Nexus 6

i got this issue when i set connect and read timeout to 1 second , as soon as timeout ,next call will throw this exception

this exception can be threw if the request thread is interrupted锛沬n my case, this is not a network issue or OkHttp issue, this is just caused by thread interrupter

As I understand now, RxJava for some reason interrupts thread on unsubscription. So if you unsubscribe during request execution, errors like this can appear.

@konmik any ideas as to how to prevent these errors? They're currently riddling our app.

Quick switching from different elements are the cause of a majority of these issues.

@lustigdev I do not remember how I solved this issue. Maybe I just installed an RxJava hook for unhandled exceptions or something like that.

Thanks for letting me know. If you do come across the time, is really appreciate it if you could find your exact solution. If not, absolutely no worries.

I believe we're doing something similar right now by overriding the error handler (or something similar) but it would be great to have something that completely solves the problem rather than just a bandaid.

As I understand, we cannot handle the error because we have already unsubscribed. So it ends up being unhandled and setting up the bandaid is an OK solution.

Another solution would be is to make unsubscription to not interrupt thread but in this case we will still have IO exceptions unhandled sometimes (they can still happen after unsubscription).

That makes sense. The issue is that the unsubscription causes the interrupted thread, so the subscription thread flow cannot handle the error.

How would you propose going about the second solution? In other words, how could you say, "Don't interrupt this thread on unsubscribe"? I saw something that David Karnok said where is you subscribe on the computation thread rather than the IO thread, unsubscriptions would not interrupt the thread. However, I tried that in a simple case and it did not work. The thread was still interrupted when subscribing on the computation thread.

The second solution would be to stop using exceptions for controlling code execution flow which is considered to be a bad practice for several years already. I do not think this is a reachable goal. Java and RxJava life cycles are coming to an end, it is too much of effort and too late to teach people now. Setting up hooks is easier. :D

Looking into using Kotlin's coroutines as an alternative solution.

We just put together a forwarding mechanism to avoid the thread that the Observable "owning" the Http request is running on (for lack of more robust / informed terminology) from being interrupted. It works like this:

We make a request. Something like getUser. That request will be executed. Likely by something along the lines of Observable.fromCallable... and within that callable, we make the OkHttp request. We subscribe to that Observable with a DisposableObserver, and upon receiving results from the network, forward those results to a subject (which the consumer of this result will have needed to subscribe to beforehand to receive results) and then subsequently, we dispose of the subscriber.

This solution works, but has the main drawback of requiring the requester to both request the information and observe the results separately. I'll report back here with more information if I can make something fairly lightweight with coroutines.

@konmik what exactly was the RxJava hook you have used? I am now using RxJava2 bu I am receiving this error randomly on some of my user devices. Its really annoying to have your app crashes.

I was also facing random java.io.InterruptedIOException: thread interrupted exceptions, caused by waitForIo or throwIfReached.
At last, I found that I was not using subscribeOn to make sure the call happen on threads other than main.
After adding subscribeOn(Schedulers.io()) the issue was gone.

i tried to return just empty String inside .fromCallable then i use .map to execute all logic.. it works..

In my case it's kind of expected InterruptedIOException if request is cancelled if I call dispose on Rx object: https://stackoverflow.com/questions/30727268/how-cancel-task-with-retrofit-and-rxjava

https://github.com/square/retrofit/blob/46dc939a0dfb470b3f52edc88552f6f7ebb49f42/retrofit-adapters/rxjava2/src/main/java/retrofit2/adapter/rxjava2/CallEnqueueObservable.java#L92-L95

But what if I want to unsubscribe and have request proceed so I will not miss the result from the server. Is there any mechanism provided by call adapter or retrofit itself to prevent the interruption?

Was this page helpful?
0 / 5 - 0 ratings