当Wifi连接丢失时,java.io.InputStream不会抛出IOException

时间:2016-08-21 20:11:20

标签: java android android-7.0-nougat

我的Android应用程序中的一些代码遇到了一个问题,它从URL下载了一个文件,这里是一段代码片段:

int bytesRead = 0;
final byte[] buffer = new byte[32 * 1024];
InputStream stream = httpUrlConnection.getInputStream();

try {
    while ((bytesRead = stream.read(buffer)) > 0) {
        Log.i("TAG", "Read from steam, Bytes Read: " + bytesRead);
    }
} catch (IOException ex) {
    //Recover from lost WIFI connection.
} finally {
    stream.close();
}

如果WiFi连接丢失,我的应用程序依赖InputStream.read()抛出IOException。如Java 8 documentation中所述,此方法应抛出IOException "如果输入流已关闭,或者出现其他I / O错误" 。在Android M中,这会立即发生,我的代码可以处理并从异常中恢复。在Android N中,不抛出此异常导致我的应用程序只是挂在read()方法中,它永远不会突破它。有没有其他人遇到这个问题,并以不会向后兼容的方式解决它?这是一个新的Android N bug吗?

3 个答案:

答案 0 :(得分:2)

如果连接断开,从套接字读取可以永久阻止。您需要使用读取超时。

答案 1 :(得分:0)

正如@EJP所说"如果连接断开,套接字读取可以永久阻塞,只需将此行添加到代码中,并捕获java.net.SocketTimeoutException:

int bytesRead = 0;
        final byte[] buffer = new byte[32 * 1024];
        InputStream stream = httpUrlConnection.getInputStream();
        httpUrlConnection.setConnectTimeout(5000);

        try {
            while ((bytesRead = stream.read(buffer)) > 0) {
                Log.i("TAG", "Read from steam, Bytes Read: " + bytesRead);
            }
        } catch (java.net.SocketTimeoutException ex) {
            //Recover from lost WIFI connection.
        } finally {
            stream.close();
        }

答案 2 :(得分:0)

为了避免重新发明轮子并发现自己弄清楚这个以及其他一些常见场景,我会使用高级库,例如 Volley

  

请参阅:Transmitting Network Data Using Volley

Volley以非常直接的方式为您提供有趣的事情:

  • 超时控制
  • 易于定制,例如,重试和退避
  • 控制几种错误
  • 取消请求API。您可以取消单个请求,也可以设置要取消的请求的块或范围。
  • 调试和跟踪工具。

发送请求就像这样简单(来自文档)

  final TextView mTextView = (TextView) findViewById(R.id.text);
...
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.google.com";

// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // Display the first 500 characters of the response string.
        mTextView.setText("Response is: "+ response.substring(0,500));
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        mTextView.setText("That didn't work!");
    }
});
// Add the request to the RequestQueue.
queue.add(stringRequest);

设置超时/重试策略非常简单:

stringRequest.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));

参数是:

  • 超时:指定每次重试尝试的套接字超时(毫秒)。
  • 重试次数:尝试重试的次数。
  • Back Off Multiplier:一个乘数,用于确定每次重试尝试设置为套接字的指数时间。

关于错误处理

如您所见,您正在向请求传递错误侦听器new Response.ErrorListener()。当出现错误时,Volley会在执行请求时发生错误时调用onErrorResponse回调public void onErrorResponse方法传递VolleyError对象的实例。

以下是Volley中的例外列表,摘自本文here

  • AuthFailureError - 如果您尝试执行Http Basic身份验证,则最有可能发生此错误。
  • NetworkError - 套接字断开连接,服务器关闭,DNS问题可能导致此错误。
  • NoConnectionError - 与NetworkError类似,但在设备没有互联网连接时触发,您的错误处理逻辑可以支持
  • NetworkError和NoConnectionError一起对待它们。
  • ParseError - 如果收到的JSON格式错误,则使用JsonObjectRequest或JsonArrayRequest时会生成此异常。如果您收到此错误,那么这是一个应该修复而不是被处理的问题。
  • ServerError - 服务器响应错误,很可能是4xx或5xx HTTP状态代码。
  • TimeoutError - 套接字超时,服务器太忙而无法处理请求或存在网络延迟问题。默认情况下,Volley会在2.5秒后超时请求,如果您一直收到此错误,请使用RetryPolicy。

所以你可以做像

这样的事情
  if ((error instanceof NetworkError) || (error instanceof NoConnectionError)) {

     //then it was a network error

   }