36

Android で Spring RESTjava.io.EOFExceptionテンプレートを使用すると、 が表示されます。

スタックトレースの原因は次のようになります。

Caused by: java.io.EOFException
at libcore.io.Streams.readAsciiLine(Streams.java:203)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:560)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:813)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:49)
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getStatusCode(BufferingClientHttpResponseWrapper.java:47)
at com.company.util.LoggingClientHttpRequestInterceptor.intercept(LoggingClientHttpRequestInterceptor.java:33)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at com.company.api.interceptor.AuthTokenInterceptor.intercept(AuthTokenInterceptor.java:51)
at org.springframework.http.client.InterceptingClientHttpRequest$RequestExecution.execute(InterceptingClientHttpRequest.java:81)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:67)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:46)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:63)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:475)
... 14 more

別の同様のスタックトレース:

org.springframework.web.client.ResourceAccessException: I/O error: null; nested exception is java.io.EOFException
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:490)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:438)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:414)
at com.company.api.ApiClient_.logLoginAttempt(ApiClient_.java:299)
at com.company.security.CompanyAuthenticationService$2.onCreateCall(CompanyAuthenticationService.java:206)
at com.company.api.SafeApiCall.doInBackground(SafeApiCall.java:49)
at com.company.api.SafeApiCall.doInBackground(SafeApiCall.java:22)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:137)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
at java.lang.Thread.run(Thread.java:856)
Caused by: java.io.EOFException
at libcore.io.Streams.readAsciiLine(Streams.java:203)
at libcore.net.http.HttpEngine.readResponseHeaders(HttpEngine.java:560)
at libcore.net.http.HttpEngine.readResponse(HttpEngine.java:813)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:274)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:486)
at org.springframework.http.client.SimpleClientHttpResponse.getRawStatusCode(SimpleClientHttpResponse.java:49)
at org.springframework.http.client.SimpleClientHttpResponse.getStatusCode(SimpleClientHttpResponse.java:55)
at org.springframework.http.client.BufferingClientHttpResponseWrapper.getStatusCode(BufferingClientHttpResponseWrapper.java:47)
at org.springframework.web.client.DefaultResponseErrorHandler.hasError(DefaultResponseErrorHandler.java:46)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:476)
... 13 more

これはすべて、私の Xoom タブレットにインストールされた Android 4.1.2 で発生しています。

問題が現れては消えます。長いリクエストによってもトリガーされません。サーバー部分は、ローカル ネットワーク内のマシンで実行されています。を介して API 呼び出しを実行しようとするとcurl、問題なく動作します。

認証トークンインターセプター:

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] data, ClientHttpRequestExecution execution) throws IOException {
    HttpHeaders headers = request.getHeaders();
    if (!StringUtils.isEmpty(mAuthToken)) {
        headers.add((mIsOAuth ? "Authorization" : "authToken"), (mIsOAuth ? "Bearer " : "") + mAuthToken);
    }
    return execution.execute(request, data);
}

LoggingClientHttpRequestInterceptor :

/** {@inheritDoc} */
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
    Log.d(TAG, "To     : " + httpRequest.getURI());
    Log.d(TAG, "Method : " + httpRequest.getMethod().name());
    Log.d(TAG, "Data   : " + new String(bytes));

    for (Object key : httpRequest.getHeaders().keySet()) {
        Log.d(TAG, "Header <" + key + ">: " + httpRequest.getHeaders().get(key));
    }

    final ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);

    if (response != null) {
        Log.d(TAG, "Response: " + response.getStatusCode());
        if (response.getBody() != null) {
            Log.d(TAG, "Response: " + convertStreamToString(response.getBody()));
        }
    } else {
        Log.d(TAG, "Response: " + response);
    }

    return response;
}

Rest テンプレートは次のように構成されます。

final RestTemplate template = new RestTemplate(false);
template.getMessageConverters().add(new MappingJacksonHttpMessageConverter());
template.setRequestFactory(new BufferingClientHttpRequestFactory(template.getRequestFactory()));
ApiUtils.addAuthTokenHeaderToRestTemplate(template, mAuthToken, false);
ApiUtils.addRequestLoggingToRestTemplate(template);

ここでクラッシュした問題の API 呼び出しは、Android アノテーション ベースのインターフェースで説明されています。

@Post("/user/memberships")
@Accept(MediaType.APPLICATION_JSON)
CompanyApiResponse saveGroupMembership(UserGroupMembership membership) throws RestClientException;

私が試したこと:

  • LoggingInterceptor を削除しました
  • すべての API 呼び出しを CURL で呼び出す
  • 呼び出し BufferingClientHttpRequestFactory を削除 -少しは役に立ちましたが、エラーは引き続き発生します。
  • Android 2.3でテストしました-エラーを再現できません

私はさまざまなフォーラムの投稿を読んでいます.URLが正しくない場合、EOF例外が表示されるようです.この場合、これを再確認しました.

また、EOF 例外が発生すると、呼び出しはサーバー側に到達しません。

修正の検索を続行するのに適したポイントはどこですか? これは Android 4.1 の不具合ですか?

この問題をデバッグしているときに、以前は実際のエラー (EOF) が表示されなかったhttps://jira.springsource.org/browse/ANDROID-102も見つかりました。


更新: http://code.google.com/p/google-http-java-client/issues/detail?id=116が見つかりました - 関連している可能性があります。

修正はhttps://codereview.appspot.com/6225045/でも概説されているため、4.1 にマージされた可能性があります。

4

4 に答える 4

59

これは、Jelly Bean 4.2 を実行している私にも噛み付きました。調査した結果、Keep-Alive が設定されていることと、HttpURLConnection であると思われる標準の J2SE HTTP クライアントを使用していることが原因で発生しているようです。

正しいと確認できる解決策が 2 つあります。

1) キープアライブをオフにします。
私にとっては、セバスチャンの答えSystem.setProperty("http.keepAlive", "false");で与えられた解決策です。うまくいきませんでした。私は使用しなければならなかった

HttpHeaders headers = new HttpHeaders();
headers.set("Connection", "Close");

RestTemplate の HttpEntity でこれらのヘッダーを送信します。
前述のように、このソリューションはパフォーマンスに影響を与える可能性があります

2) HTTP クライアントを変更します。
Spring for Android (1.0.1.RELEASE でテストされていますが、以前のリリースでも可能でした) では、RestTemplate インスタンスのデフォルトの HTTP クライアントは、デバイス上の Android のバージョンによって決まります。API 9 以降は HttpURLConnection を使用し、それ以前は HTTPClient を使用します。クライアントを明示的に古いものに設定するには、次を使用します

restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());

詳細はこちら: http://static.springsource.org/spring-android/docs/1.0.1.RELEASE/reference/htmlsingle/#d4e34
これがパフォーマンスに与える影響はわかりませんが、機能しないアプリよりもパフォーマンスが高いと思います。

とにかく、それが誰かを助けることを願っています。私はこれを追い詰めて1週間無駄にしました。

于 2012-12-20T22:47:44.817 に答える
11

http://code.google.com/p/google-http-java-client/issues/detail?id=116には、最新のコメントに回避策が含まれています。

これは明らかに何らかの形で keepAlive 接続に接続されています。

私が使用する場合: System.setProperty("http.keepAlive", "false"); 問題が消えます。

しかし、私の理解では、キープアライブ接続はパフォーマンスを大幅に向上させるため、無効にしない方がよいでしょう。

また、古いバージョンではキープアライブを無効にする必要があることも認識していましたが、私のデバイスは Jelly Bean です。

適用すると、エラーは消えました。
Spring とは完全に関係していないようですが、JB の問題です。

于 2012-11-08T16:53:31.443 に答える
0

最近、この問題に直面しましたが、次のコードでヘッダーを設定すると、この問題を解決できます。

headers.set("Accept-Language", "en-US,en;q=0.8");
于 2014-04-28T10:49:22.053 に答える
-1
RestTemplate restTemplate = new RestTemplate();
            ((SimpleClientHttpRequestFactory)restTemplate.getRequestFactory()).setOutputStreaming(false);

restTemplate.postForObject......
于 2016-03-24T09:24:37.640 に答える