36

java.io.EOFException在 Android 上使用 Spring REST 模板时收到 's。

堆栈跟踪原因如下所示:

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,它工作得很好。

AuthTokenInterceptor

@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 Template 的配置如下:

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
  • 通过 CURL 调用所有 API 调用
  • 删除了调用 BufferingClientHttpRequestFactory -有一点帮助,但错误仍然发生。
  • 在 Android 2.3 上测试过 -错误无法重现

我一直在阅读各种论坛帖子,如果 URL 不正确,似乎会出现 EOF 异常,在这种情况下我会仔细检查。

另外值得注意的是,一旦发生 EOF 异常,调用甚至不会到达服务器端。

继续寻找修复的好点在哪里?这是 Android 4.1 带来的不便吗?

在调试这个问题时,我还发现了https://jira.springsource.org/browse/ANDROID-102这使我之前看不到真正的错误(EOF)。


更新:刚刚找到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使用标准 J2SE HTTP 客户端的组合,我相信它是 HttpURLConnection。

我可以确认有 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 我不确定这会对性能产生什么影响,但我猜猜它比不工作的应用程序性能更高。

无论如何,希望对某人有所帮助。我只是浪费了一周的时间去追逐这个。

于 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"); 问题消失。

但据我了解,保持活动连接会大大提高性能,因此最好不要禁用它们。

我也知道旧版本应该禁用保持活动,但我的设备是果冻豆。

一旦应用,错误就消失了。
似乎它与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 回答