根据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
或在 中的
阻塞而不阅读错误信息。这就是您在大多数示例中看到的内容。HttpURLConnection
上调用 disconnect
finally
我在一个 source code 中看到了以下评论,同时浏览 HttpURLConnection 的实现。这可能是连接在未读取所有数据的情况下关闭的原因。
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
,连接将被视为可重用并添加到连接池中。
您可以在下图中看到,变量 connection
和 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
会导致调用以 false
作为参数的此 release
方法。
最后的 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, offset, 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, offset, 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 - 当抛出 IOException 时,我们是否需要使用 HttpURLConnection 的错误流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23136475/