Robolectric + rxJava + retrofit The second call raises java.io.InterruptedIOException

I am developing an android application. I use retrofit (with OkClient) for api requests and Robolectric for testing. My api is as follows:

@GET("/v1/book/{bookId}") Observable<Book> getBook(@Path("bookId") int bookId); 

Only for Robolectric I make air calls synchronously. The constructor for the restAdapter is as follows:

 RestAdapter.Builder builder = new RestAdapter.Builder().setEndpoint(environment.getServerEndpoint()) .setClient(new OkClient(client)) .setExecutors(new ImmediateExecutor(), null) .setErrorHandler(new ErrorHandler()) .setRequestInterceptor(new RequestInterceptor() { @Override public void intercept(RequestFacade request) { // Always ask for JSON data request.addHeader("Accept", "application/json"); request.addHeader("Content-Type", "application/json"); } }); public class ImmediateExecutor implements Executor { @Override public void execute(Runnable command) { command.run(); } } 

I have a simple test that looks like this:

 API.getBook(1).subscribe(); API.getBook(2).subscribe(); 

The restadapter is created along with the constructor and the API object with it (restadapter.create (...)). I skipped this as it is trivial.

The first works without problems, but the second, which should be the same, throws an exception:

 java.io.InterruptedIOException at okio.Timeout.throwIfReached(Timeout.java:146) at okio.Okio$1.write(Okio.java:75) at okio.AsyncTimeout$1.write(AsyncTimeout.java:155) at okio.RealBufferedSink.flush(RealBufferedSink.java:201) at com.squareup.okhttp.internal.http.HttpConnection.flush(HttpConnection.java:140) at com.squareup.okhttp.internal.http.HttpTransport.finishRequest(HttpTransport.java:52) at com.squareup.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:828) at com.squareup.okhttp.internal.http.HttpEngine.access$200(HttpEngine.java:95) at com.squareup.okhttp.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:823) at com.carmacarpool.carmaridepool.rest.CarmaHttpClientFactory$NetworkLoggingInterceptor.intercept(CarmaHttpClientFactory.java:77) at com.squareup.okhttp.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:803) at com.squareup.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:684) at com.squareup.okhttp.Call.getResponse(Call.java:272) at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:228) at com.carmacarpool.carmaridepool.rest.CarmaHttpClientFactory$LoggingInterceptor.intercept(CarmaHttpClientFactory.java:53) at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:225) at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:199) at com.squareup.okhttp.Call.execute(Call.java:79) 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:471) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at com.carmacarpool.carmaridepool.testutils.ShadowUICarmaRESTClassFactory$ImmediateExecutor.execute(ShadowUICarmaRESTClassFactory.java:91) at retrofit.RxSupport$1.call(RxSupport.java:42) at retrofit.RxSupport$1.call(RxSupport.java:32) at rx.Observable.subscribe(Observable.java:7393) at rx.Observable.subscribe(Observable.java:7083) at com.carmacarpool.carmaridepool.CorridorTest.test(CorridorTest.java:99) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:265) at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:205) at org.robolectric.RobolectricTestRunner.runChild(RobolectricTestRunner.java:54) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:173) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49) at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:64) at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32) at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93) at com.sun.proxy.$Proxy2.processTestClass(Unknown Source) at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:106) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35) at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24) at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360) at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) 

I have one network interceptor with only a log and it works fine. I have access to server logs, and this request does not even reach the server.

Does anyone know what could be the problem? It seems that for some unknown reason, the thread is being killed?

Thanks.

EDIT . If in the onNext function (the first subscription parameter), I execute the second request, then it works. Everything is in sync as expected.

SOLUTION After many attempts, I could find a solution. The problem seems to come from Okio. Apparently there is a buffer that writes the answer (or something like this, I decided it a few weeks ago, and I don’t remember 100%). This buffer closes in the middle of the second request and causes an error.

To fix this, I complete the request in a try / catch block. If an IOException occurs, I repeat. I repeat a maximum of 5 times (to avoid endless loops).

The code looks like this:

  try { // Perform the request return chain.proceed(request); } catch (IOException e) { // Retry again if we haven't tried at least REQUEST_RETRIES times if (iteration < REQUEST_RETRIES) { return performRequest(chain, ++iteration); } // Otherwise, save the exception and throw it later exception = e; } 

The target comes from the OkHttpClient interceptor:

OkHttpClient client = new OkHttpClient (); client.interceptors (). add (new CustomInterceptor ());

the private class CustomInterceptor implements the Interceptor {@Override public Response intercept (throws a chain) throws an IOException {...

Hope this will be helpful.

+6
source share
1 answer

I think you are doing too much to make observables work synchronously during testing. Instead, all you have to do is during the test.

  Book book = retrofitAPI.getBook(someId).toBlocking.first() 

This would execute the observed synchronously. One of my favorite parts of the Rx library.

+2
source

Source: https://habr.com/ru/post/985346/


All Articles