Android(Java)HttpURLConnection静默重试' read'超时

时间:2014-11-23 21:29:29

标签: java android httpurlconnection android-volley

所以我使用Google Volley进行HTTP请求,主要使用Java' s HttpURLConnection

根据我的测试,问题是这个
当阅读'在HttpURLConnection到达时超时,在连接关闭之前执行静默重试并抛出相关异常(SocketTimeoutException)。

注意
HTTP POST请求时发现此错误 - '阅读'超时不同于' connect'超时。
- 如果'阅读'超时(通过调用connection.setReadTimeout(int)设置)未设置(0),或设置为大于connection.setConnectTimeout(int)的值,不会发生此错误。
- 已经讨论过这个问题,例如here,但我还没有找到任何令人满意的解决方案。
- 可以找到一个有点相关的问题here,但我不确定它是否相关(是吗?)

更多背景
我的应用程序用于付钱,所以不重试请求是至关重要的(是的,我知道它可以由服务器处理,我希望我的客户是"正确"无论如何)。

当阅读'如果建立了服务器连接,则设置超时,但服务器等待/休眠/延迟响应“超时”#39;在回答之前的时间(从而引发“读取”异常而不是“连接”异常),在异常引发之前发送另一个(静默)请求,产生2个类似的请求,这是不可接受的。

我在寻找什么样的解决方案?
好吧,一个能很好地解决这个问题/错误的问题,就像修复解释here一样(但我再说一遍,我觉得这种情况与此无关)。 此外,我希望保持原始流程不变,这意味着不要强制连接关闭或类似的东西。

我现在要做的是设置“阅读”#39;超时至'连接的两倍'超时(它们同时开始计数),以确保连接'首先引发异常。我还将尝试在服务器端克服此问题。问题是,这个'阅读'超时是有原因的,我当前的实现实际上只是忽略它,并且只处理'连接'超时。

修改 VolleyRetryPolicy对此问题没有影响,因为这是静默重试。 我在图书馆里看起来尽可能深。到处记录/断点,取消了重试的调用。据我所知,这是HttpURLConnection问题的99.99%。

4 个答案:

答案 0 :(得分:8)

这个错误的决定是由开发人员在2006年做出的。 这是一个很好的引用,从java的角度来解释整个情况:

  

"你可能已经猜到了它是一个错误(http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6382788)。当然,这不是重试机制,只是废话。问题是它也适用于POST(默认情况下,每个HTTP RFC都不是幂等的)。但不要担心,比尔很久以前就修复了这个错误。比尔通过引入一个切换来修复它。 Bill了解了向后兼容性。比尔决定最好离开#39;默认情况下,这会使它与bug向后兼容。比尔笑了。他已经可以看到全球各地惊讶的开发人员的脸。请不要像比尔?"
  Source

建议的解决方案是:

System.setProperty("sun.net.http.retryPost", "false")

但是我们不能在android上做到这一点!唯一剩下的解决方案是:

httpURLConnection.setChunkedStreamingMode(0);

Which seems to be working, but isn't very effective from a requesting-time perspektive.

编辑: 我无法使用此实现,所以我寻找一个替代库。 我发现自Android 4.4以来HttpUrlConnection的实现使用OkHttp。由于OkHttp是opensource,我可以搜索他们是否也有静默重试问题。他们是had problems with it并在2016年4月修复了它。CommonsWare(一个真实的android expert)解释说每个制造商都可以决定他们可以使用哪种实现。因此,必须有很多设备在POST请求上进行静默重试,作为开发人员,我们只能尝试一些workarrounds。

我的解决方案是更改库

编辑2:给你一个最终答案:You can now use OkHttp as the transport layer for Volley with minimal code.

Another helpful solution

答案 1 :(得分:3)

好吧,很多时间都过去了,所以我想我会回答当前(不完美)的解决方案,让其他人轻松看到它(也写在问题中)。

只需将readTimeout设置为0(无超时)或至少设置连接超时值。这将确保在读取超时之前引发连接超时。

int timeoutMs = request.getTimeoutMs();    //  or whatever
connection.setConnectTimeout(timeoutMs);   //  actual timeout desired
connection.setReadTimeout(timeoutMs);      //  >= timeoutMs or 0

当然,这取决于请求的使用。大多数情况下,应用程序是包含相关服务器的项目的一部分,并且您知道无论如何都不会发生读取超时。

如上所述,并非真正的解决方案",而是一个非常好的,90%有用的解决方案。

答案 2 :(得分:1)

您可以查看Volley的DefaultRetryPolicy类。

答案 3 :(得分:0)

As it says in one of the links you quoted,您需要将sun.net.http.retryPost设置为false

或者,不要使用固定长度或分块传输模式:请参阅this bug report