sto sviluppando e android app. Sto usando il retrofit (con OkClient) per le richieste API e Robolectric per i test. La mia api assomiglia a questa:Robolectric + rxJava + retrofit Seconda chiamata tiri java.io.InterruptedIOException
@GET("/v1/book/{bookId}") Observable<Book> getBook(@Path("bookId") int bookId);
Solo per Robolectric sto forzando le chiamate api per essere sincrone. Il costruttore restAdapter assomiglia a questo:
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();
}
}
Ho un semplice test che assomiglia a questo:
API.getBook(1).subscribe();
API.getBook(2).subscribe();
Restadapter viene creato con il costruttore, e l'oggetto API con esso (restadapter.create (. ..)). L'ho omesso perché è banale.
La prima funziona senza problemi, ma la seconda quella che dovrebbe essere la stessa genera un'eccezione:
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)
Ho un intercettore di rete solo con un log e funziona bene. Ho accesso ai log del server e la richiesta non raggiunge nemmeno il server.
Qualcuno ha idea di quello che potrebbe essere il problema? Sembra che per qualche ragione sconosciuta il thread venga ucciso?
Grazie.
EDIT: Se nella funzione onNext (primo parametro di subscribe), eseguo la seconda richiesta, quindi funziona. Tutto è sincrono come previsto.
SOLUZIONE Dopo molti tentativi, ho potuto trovare una soluzione. Il problema sembra provenire da Okio. Apparentemente c'è un buffer che scrive la risposta (o qualcosa del genere, l'ho risolto poche settimane fa e non ricordo il 100%). Questo buffer viene chiuso nel mezzo della seconda richiesta e questo è ciò che sta causando l'errore.
Per risolvere il problema, ciò che faccio è racchiudere la richiesta in un blocco try/catch. Se si verifica una IOException, riprovo. Sto riprovando un massimo di 5 volte (per evitare loop infiniti).
Il codice si presenta come:
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; }
L'oggetto catena proviene da un intercettore OkHttpClient:
OkHttpClient client = new OkHttpClient(); client.interceptors(). Add (new CustomInterceptor());
classe CustomInterceptor privato implementa Interceptor {@Override pubblico intercetta Response (catena Chain) throws IOException {...
Spero che questo è utile.
Solo due centesimi sui test: perché non provare senza un vero networking? –
Questo può anche aiutare - https://groups.google.com/forum/#!topic/robolectric/-HVhS3rnU54 –