最近遇到了这样一个问题,在Android开启StrictMode的时候,会抛出一个异常如下:
04-01 16:07:56.864: E/StrictMode(26867): A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
04-01 16:07:56.864: E/StrictMode(26867): java.lang.Throwable: Explicit termination method 'close' not called
04-01 16:07:56.864: E/StrictMode(26867): java.lang.Throwable: Explicit termination method 'close' not called
04-01 16:07:56.864: E/StrictMode(26867): at dalvik.system.CloseGuard.open(CloseGuard.java:184)
04-01 16:07:56.864: E/StrictMode(26867): at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:271)
04-01 16:07:56.864: E/StrictMode(26867): at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl.java:598)
04-01 16:07:56.864: E/StrictMode(26867): at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:560)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.io.SocketInputBuffer.(SocketInputBuffer.java:70)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:83)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:170)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:106)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:129)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:172)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
04-01 16:07:56.864: E/StrictMode(26867): at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:271)
04-01 16:07:56.864: E/StrictMode(26867): at com.android.org.conscrypt.OpenSSLSocketImpl.waitForHandshake(OpenSSLSocketImpl.java:598)
04-01 16:07:56.864: E/StrictMode(26867): at com.android.org.conscrypt.OpenSSLSocketImpl.getInputStream(OpenSSLSocketImpl.java:560)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.io.SocketInputBuffer.(SocketInputBuffer.java:70)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.SocketHttpClientConnection.createSessionInputBuffer(SocketHttpClientConnection.java:83)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.DefaultClientConnection.createSessionInputBuffer(DefaultClientConnection.java:170)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.SocketHttpClientConnection.bind(SocketHttpClientConnection.java:106)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.DefaultClientConnection.openCompleted(DefaultClientConnection.java:129)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:172)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
04-01 16:07:56.864: E/StrictMode(26867): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
异常大概是说资源没有释放,需要显示的调用close方法。从log上看是网络访问时出现的问题(我使用的是HttpClient)。
但是在关闭StrictMode的时候程序可以完全正常的运行。原因何在呢?
通过查看HttpClient官方文档发现,在HttpClient请求结束后需要销毁HttpEntity,这样才可以使网络连接返回连接池等待下一次重用(如果不需要重用的话也可以直接关掉连接)。
官方推荐的HttpEntity销毁方法:HttpEntity#consumeContent
在查看代码时发现,HttpEntity并没有销毁。可是其他网络请求部分的HttpEntity也都没有销毁,为什么只有这一个网络请求会报错呢?而且代码中有监听网络超时并关掉连接的处理,为什么还会出现这个问题呢?
仔细查看发现,出现问题的部分与其他部分稍有不同。出现问题的网络请求在解析响应的时候,并不需要解析Entity,他的响应结果是放在Headers里面的。也就是说这部分代码在请求结束的时候没有调用HttpResponse#getEntity#getContent方法。
查看HttpClient文档发现getContent方法也可以使Entity销毁。
所以就是这个原因使StrictMode抛出资源未释放的异常。
因为有了网络超时监听的处理,所以在关闭StrictMode的时候并不会出现什么问题,因为没有释放的连接会等到网络超时的时候会被释放。
在使用HttpClient时一定要注意资源的释放。即使有网络超时自动关闭连接的监听,StrictMode也会报告没有释放资源的异常,因为连接不会立即释放,需要等待超时时间的到来才会release,这样影响了程序的性能。
附HttpClient源码分析:
1.BasicHttpEntity#getContent源码:
在getContent方法中将contentObtained置为true;标记content已经被获取过了,在其他地方检测这个标记,如果资源被获取过,那么就会release掉这个连接。
2.BasicHttpEntity#consumeContent源码:
关闭content(content类型为InputStream)
3.EntityUtils#toString
在EntityUtils#toString方法的最后直接进行了reader.close();因此直接调用EntityUtils#toString方法后相当于调用了HttpEntity#consumeContent方法