2015-04-17 16 views
6

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.

+2

Solo due centesimi sui test: perché non provare senza un vero networking? –

+0

Questo può anche aiutare - https://groups.google.com/forum/#!topic/robolectric/-HVhS3rnU54 –

risposta

2

Penso che stai facendo un po 'troppo per ottenere osservabili da eseguire in modo sincrono durante i test.Invece tutto ciò che devi fare è durante il tuo test

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

Questo eseguirà l'osservabile in modo sincrono. Una delle mie parti preferite della libreria Rx.

+0

Questo non funziona per me, poiché nel mio test creo un'attività, ed è l'attività che chiamare il servizio che esegue la richiesta. Quindi non posso fare aBlocking(). First(). Lo sto facendo quando provo il servizio stesso. –

Problemi correlati