android – 抛出IOException时我们是否需要使用HttpURLConnection的错误流

前端之家收集整理的这篇文章主要介绍了android – 抛出IOException时我们是否需要使用HttpURLConnection的错误流前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
根据Oracle Java的技术指南,我们应该在抛出IOException时使用HttpURLConnection的错误

http://docs.oracle.com/javase/6/docs/technotes/guides/net/http-keepalive.html

What can you do to help with Keep-Alive? Do not abandon a connection
by ignoring the response body. Doing so may results in idle TCP
connections. That needs to be garbage collected when they are no
longer referenced.

If getInputStream() successfully returns,read the entire response
body.

When calling getInputStream() from HttpURLConnection,if an
IOException occurs,catch the exception and call getErrorStream() to
get the response body (if there is any).

Reading the response body cleans up the connection even if you are not
interested in the response content itself. But if the response body is
long and you are not interested in the rest of it after seeing the
beginning,you can close the InputStream. But you need to be aware
that more data could be on its way. Thus the connection may not be
cleared for reuse.

Here’s a code example that complies to the above recommendation:

这是代码示例

try {
        URL a = new URL(args[0]);
        URLConnection urlc = a.openConnection();
        is = conn.getInputStream();
        int ret = 0;
        while ((ret = is.read(buf)) > 0) {
          processBuf(buf);
        }
        // close the inputstream
        is.close();
} catch (IOException e) {
        try {
                respCode = ((HttpURLConnection)conn).getResponseCode();
                es = ((HttpURLConnection)conn).getErrorStream();
                int ret = 0;
                // read the response body
                while ((ret = es.read(buf)) > 0) {
                        processBuf(buf);
                }
                // close the errorstream
                es.close();
        } catch(IOException ex) {
                // deal with the exception
        }
}

这适用于Android平台吗?因为我在大多数Android代码示例中都没有看到这样的技术.

解决方法

如果您不想向用户显示错误消息,请关闭InputStream或在finally块中调用HttpURLConnection上的disconnect,而不读取错误消息.这是您在大多数示例中看到的内容.

在浏览HttpURLConnection的实现时,我在其中一个source code中发现了以下评论.这可能是在不读取所有数据的情况下关闭连接的原因.

This should be invoked when the connection is closed unexpectedly to
invalidate the cache entry and to prevent the HTTP connection from
being reused. HTTP messages are sent in serial so whenever a message
cannot be read to completion,subsequent messages cannot be read
either and the connection must be discarded.

根据Android的HttpURLConnection实现,如遇异常:

>如果未读取错误且InputStream已关闭,则连接将被视为不可重复使用并关闭.
>如果您读取错误然后关闭InputStream,则连接被视为可重用,并被添加到连接池.

您可以在下图中看到可变连接&只要读取所有数据,connectionReleased分别设置为null和true.请注意,getErrorStream返回InputStream,因此它在异常场景中也是有效的.

代码分析:让我们看看FixedLengthInputStream的一个专门的InputStream实现.这是close方法实现:

@Override public void close() throws IOException {
    if (closed) {
        return;
    }
    closed = true;
    if (bytesRemaining != 0) {
        unexpectedEndOfInput();
    }
 }

实例变量bytesRemaining包含要读取的InputStream上仍可用的字节数.这是unexpectedEndOfInput方法实现:

protected final void unexpectedEndOfInput() {
    if (cacheRequest != null) {
        cacheRequest.abort();
    }
    httpEngine.release(false);
}

这是release方法实现.在HttpURLConnection实例上调用disconnect将导致调用此release方法,并将false作为参数.
最后一次if检查确保是否需要关闭连接或将连接添加到连接池以供重用.

public final void release(boolean reusable) {
    // If the response body comes from the cache,close it.
    if (responseBodyIn == cachedResponseBody) {
        IoUtils.closeQuietly(responseBodyIn);
    }
    if (!connectionReleased && connection != null) {
        connectionReleased = true;
        // We cannot reuse sockets that have incomplete output.
        if (requestBodyOut != null && !requestBodyOut.closed) {
            reusable = false;
        }
        // If the headers specify that the connection shouldn't be reused,don't reuse it.
        if (hasConnectionCloseHeader()) {
            reusable = false;
        }
        if (responseBodyIn instanceof UnknownLengthHttpInputStream) {
            reusable = false;
        }
        if (reusable && responseBodyIn != null) {
            // We must discard the response body before the connection can be reused.
            try {
                Streams.skipAll(responseBodyIn);
            } catch (IOException e) {
                reusable = false;
            }
        }
        if (!reusable) {
            connection.closeSocketAndStreams();
            connection = null;
        } else if (automaticallyReleaseConnectionToPool) {
            HttpConnectionPool.INSTANCE.recycle(connection);
            connection = null;
        }
    }
}

您共享的代码,在其中处理IOException,读取错误流然后关闭,确保Connection可重用并添加到连接池.从InputStream读取所有数据的那一刻,Connection将添加到连接池中.这是FixedLengthInputStream的read方法实现:

@Override public int read(byte[] buffer,int offset,int count) throws IOException {
        Arrays.checkOffsetAndCount(buffer.length,offset,count);
        checkNotClosed();
        if (bytesRemaining == 0) {
            return -1;
        }
        int read = in.read(buffer,Math.min(count,bytesRemaining));
        if (read == -1) {
            unexpectedEndOfInput(); // the server didn't supply the promised content length
            throw new IOException("unexpected end of stream");
        }
        bytesRemaining -= read;
        cacheWrite(buffer,read);
        if (bytesRemaining == 0) {
            endOfInput(true);
        }
        return read;
    }

当bytesRemaining变量变为0时,调用endOfInput,这将使用true参数进一步调用release方法,这将确保连接被合并.

protected final void endOfInput(boolean reuseSocket) throws IOException {
        if (cacheRequest != null) {
            cacheBody.close();
        }
        httpEngine.release(reuseSocket);
    }

猜你在找的Android相关文章