2015-04-17 348 views
6

我正在開發和Android應用程序。我使用改進(與OkClient)的API請求和Robolectric進行測試。我的API看起來是這樣的:Robolectric + rxJava +翻新第二次調用拋出java.io.InterruptedIOException

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

只是爲Robolectric我強迫api調用是同步的。該restAdapter建設者看起來是這樣的:

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(); 
     } 
    } 

我有一個簡單的測試,看起來像這樣:

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

Restadapter與製造商創建的,它的API對象(restadapter.create(。 ..))。我忽略了它,因爲它是微不足道的。

的第一個作品沒有問題,但第二個應該是相同拋出異常:

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) 

我有一個網絡攔截器只是一個日誌,它工作正常。我有權訪問服務器日誌,並且請求甚至沒有到達服務器。

有沒有人有任何線索可能是什麼問題?這似乎是因爲某種未知的原因導致線程被殺害?

謝謝。

編輯:如果在onNext函數(訂閱的第一個參數),我執行第二個請求,然後它的工作。如預期一切都是同步的。

解決方案 經過多次嘗試,我可以找到解決方案。這個問題似乎來自Okio。顯然有一個緩衝區寫回應(或類似的東西,我幾個星期前解決了它,但我不記得100%)。這個緩衝區在第二個請求的中間被關閉,這就是導致錯誤的原因。

要解決此問題,我所做的是將請求包裝在try/catch塊中。如果發生IOException,那麼我重試。我最多重試5次(以避免無限循環)。

代碼看起來像:

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; 
    } 

鏈對象來自一個OkHttpClient攔截:

OkHttpClient客戶=新OkHttpClient(); client.interceptors()。add(new CustomInterceptor());

私有類CustomInterceptor實現攔截器@覆蓋 公開回應攔截(連鎖鏈)拋出IOException異常{...

希望這是有幫助的。

+2

關於您的測試只需兩美分 - 爲什麼不測試沒有真正的網絡? –

+0

這也可以幫助 - https://groups.google.com/forum/#!topic/robolectric/-HVhS3rnU54 –

回答

2

我認爲你在測試過程中做了太多的事情來讓observables同步運行。相反,所有你需要做的是在你的測試中

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

這將執行observable同步。 Rx庫中我最喜歡的部分之一。

+0

這對我不起作用,因爲在我的測試中,我創建了一個活動,這是活動的人調用執行請求的服務。所以我不能做到阻塞()。first()。當我測試服務本身時,我正在做這件事。 –

相關問題