URL url
=
new
URL(
"xxxxx"
);
connection
=
(HttpURLConnection) url
.
openConnection();
connection
.
connect();
...
...
...
...
...
} catch (IOException e) {
e
.
printStackTrace();
} finally {
if
(connection
!=
null
) {
connection
.
disconnect();
最近在8.0的手机里跑类似上述代码时,突然发现会概率性地打印类似如下的log:
A connection to xxxxxx was leaked. Did you forget to close a response body?
仔细check了一下代码,发现connection用完后,已经disconnect了,
怎么还会打印这种让人觉得不太舒服的代码?
为了解决这个问题,在国内外的网站上找了很久,
但都没能找到真正可行的解决方案。
无奈之下,只好硬撸了一边源码,总算是找到了问题的原因和一个解决方案。
因此,在本片博客中记录一下比较重要的地方。
Android的源码中,我们知道URL的openConnection函数的底层实现依赖于OkHttp库,
对于这部分的流程,我之后专门写一篇文档记录一下。
现在我们需要知道的是:
OkHttp库中的创建的Http链接为RealConnection对象。
为了达到复用的效果,OkHttp专门创建了ConnectionPool对象来管理所有的RealConnection。
这有点像线程池会管理所有的线程一样。
当我们创建一个新的RealConnection时,会调用ConnectionPool的put函数:
void put(RealConnection connection) {
assert (Thread.holdsLock(this));
if (connections.isEmpty()) {
executor.execute(cleanupRunnable);
connections.add(connection);
现在,我们来看看cleanupRunnable会干些啥:
private Runnable cleanupRunnable = new Runnable() {
@Override public void run() {
while (true) {
long waitNanos = cleanup(System.nanoTime());
if (waitNanos == -1) return;
if (waitNanos > 0) {
long waitMillis = waitNanos / 1000000L;
waitNanos -= (waitMillis * 1000000L);
synchronized (ConnectionPool.this) {
try {
ConnectionPool.this.wait(waitMillis, (int) waitNanos);
} catch (InterruptedException ignored) {
cleanup函数的真面目如下:
long cleanup(long now) {
int inUseConnectionCount = 0;
int idleConnectionCount = 0;
RealConnection longestIdleConnection = null;
long longestIdleDurationNs = Long.MIN_VALUE;
synchronized (this) {
for (Iterator<RealConnection> i = connections.iterator(); i.hasNext(); ) {
RealConnection connection = i.next();
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++;
continue;
idleConnectionCount++;
long idleDurationNs = now - connection.idleAtNanos;
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs;
longestIdleConnection = connection;
if (longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections) {
connections.remove(longestIdleConnection);
} else if (idleConnectionCount > 0) {
return keepAliveDurationNs - longestIdleDurationNs;
} else if (inUseConnectionCount > 0) {
return keepAliveDurationNs;
} else {
return -1;
Util.closeQuietly(longestIdleConnection.getSocket());
return 0;
通过cleanup函数,不难看出该函数主要的目的就是:
逐步清理connectionPool中已经空闲的RealConnection。
现在唯一的疑点就是上文中的pruneAndGetAllocationCount函数了:
* Prunes any leaked allocations and then returns the number of remaining live allocations on
* {@code connection}. Allocations are leaked if the connection is tracking them but the
* application code has abandoned them. Leak detection is imprecise and relies on garbage
* collection.
private int pruneAndGetAllocationCount(RealConnection connection, long now) {
List<Reference<StreamAllocation>> references = connection.allocations;
for (int i = 0; i < references.size(); ) {
Reference<StreamAllocation> reference = references.get(i);
if (reference.get() != null) {
continue;
Internal.logger.warning("A connection to " + connection.getRoute().getAddress().url()
+ " was leaked. Did you forget to close a response body?");
references.remove(i);
connection.noNewStreams = true;
if (references.isEmpty()) {
connection.idleAtNanos = now - keepAliveDurationNs;
return 0;
return references.size();
从上面的代码可以看出,pruneAndGetAllocationCount发现没有被引用的RealConnection时,
就会打印上文提到的leaked log。
个人猜测,如果开头的代码执行完毕后,GC先回收HttpURLConnection(非直接持有)等持有RealConnection的对象,后回收RealConnection。
且在回收HttpURLConnection后,回收RealConnection前,刚好执行了pruneAndGetAllocationCount,就可能会打印这种log。
这也是注释中提到的,pruneAndGetAllocationCount依赖于GC。
不过从代码来看,这并没有什么问题,Android系统仍会回收这些资源。
在文章开头的代码中,最后调用的HttpURLConnection的disconnect函数。
该函数仅会调用StreamAllocation的cancel函数,且最终调用到RealConnection的cancel函数:
public void cancel() {
Util.closeQuietly(rawSocket);
可以看出,该方法仅关闭了socket,并没有移除引用,不会解决我们遇到的问题。
经过不断地尝试和阅读源码,我发现利用下述方式可以解决这个问题:
HttpURLConnection connection = null;
try {
URL url = new URL("xxxxx");
connection = (HttpURLConnection) url.openConnection();
connection.connect();
...............
} catch (IOException e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.getInputStream().close();
} catch (IOException e) {
e.printStackTrace();
connection.disconnect();
当我们主动关闭HttpURLConnection的inputStream时,将会先后调用到StreamAllocation的noNewStreams和streamFinished函数:
public void noNewStreams() {
deallocate(true, false, false);
public void streamFinished(HttpStream stream) {
synchronized (connectionPool) {
if (stream == null || stream != this.stream) {
throw new IllegalStateException("expected " + this.stream + " but was " + stream);
deallocate(false, false, true);
private void deallocate(boolean noNewStreams, boolean released, boolean streamFinished) {
RealConnection connectionToClose = null;
synchronized (connectionPool) {
if (streamFinished) {
this.stream = null;
if (released) {
this.released = true;
if (connection != null) {
if (noNewStreams) {
connection.noNewStreams = true;
if (this.stream == null && (this.released || connection.noNewStreams)) {
release(connection);
if (connection.streamCount > 0) {
routeSelector = null;
if (connection.allocations.isEmpty()) {
connection.idleAtNanos = System.nanoTime();
if (Internal.instance.connectionBecameIdle(connectionPool, connection)) {
connectionToClose = connection;
connection = null;
if (connectionToClose != null) {
Util.closeQuietly(connectionToClose.getSocket());
private void release(RealConnection connection) {
for (int i = 0, size = connection.allocations.size(); i < size; i++) {
Reference<StreamAllocation> reference = connection.allocations.get(i);
if (reference.get() == this) {
connection.allocations.remove(i);
return;
throw new IllegalStateException();
到此,我们终于知道出现该问题的原因及对应的解决方案了。
上述代码省略了HttpURLConnection及底层OkHttp的许多流程,
仅给出了重要的部分,后续我会专门写一篇博客来补充分析这部分代码。
这个问题说实话,个人感觉并不是很重要,
但想真正明白原理,还是需要细致阅读源码的。
一旦真正搞懂,确实有点GAI爷歌里的感觉:
一往无前虎山行,拨开云雾见光明。
Android-杰里-OkHttp
Jerry只是Android中OKHTTP的实用工具,它具有FLUENT APIS 。
从ok-http 3开始,square已更改了许多API,因此我仍在开发新版本的Jerry。 我待会儿给一个样品。
这很简单! 我也在我的书房里给了一个枫糖。
RequestParams params = new RequestParams();
params.put("key", "value");
try {
params.put("fileKey",new File("filepath"));
} catch (FileNotFoundException e) {
e.printStackTrace();
安卓开发网络请求可谓是安卓开发的灵魂,如果你不会网络请求,那么你开发的应用软件就是一具没有灵魂的枯骨。 在安卓开发中进行网络请求和java中的网络请求有异曲同工之妙,但是安卓软件毕竟的安装在我们手机上的,而平常的应用软件下载后会要求你给与权限,否则就没办法使用,网络请求也需要对应的权限,否则就没法进行联网操作。 首先在AndroidManifest.xml文件中添加网络请求权限。要在manifest标签内,application标签外添加 新建java文件,创建静态方法,返回请求后的结果。
最近在一个程序中使用okhttp调用http接口。开始时一切正常,但是测试运行一段时间后,okhttp就会报告recv失败。同时在调用端机器上,netstat显示很多套接字是TIMEWAIT状态。原来每次调用接口,okhttp都建立了一个新连接。而被调用的服务器在连接超过一定数量后会拒绝服务。
最初的想法是用连接池降低连接数。
OkHttpClient httpClient = new OkHttpClient.Builder()
.connectionPool(new Connection
下面是报错内容,使用okhttp,的时候报错的.这个不关闭,好像时间久了会报内存溢出错误.
W/OkHttp: A connection to http://172.19.128.64:8061/ was leaked. Did you forget to close a response body?
比如,下面这样解决:
//4.同步人脸库操作
private void faceAsync() {
//1.这里去同步照片到人脸库
//String ...
/** Returns a recycled connection to {@code address}, or null if no such connection exists. */
RealConnection get(Address address, StreamAllocation streamAllocation) {
assert (Thread.holdsLock(this));
for (RealConnection connection : connections) {
if (con
概述java的Closeable和Cloneable两个接口,一字之差,但是要完成的功能却基本没有什么联系。最大的相同点在于,这两个就是java基本类库有特殊处理的接口,不按规则来,加班...
但使用中遇到问题:
1.在公司开发团队开发,有可能会遇到不能连外网的情况,使我们无法下载Maven构件
2.公司的开发团队比较大,下载的Maven构件也比较多,每人都需要连接外网的中央仓库去下载构件,外网带宽占用很大,下载速度很慢,影响工作效率。
3.一些第三方构件数量多,各项目使用比
这个类主要是用来配置okhttp这个框架的,通俗一点讲就是这个类是管理这个框架的各种设置的。Call 类的工厂,通过 OkHttpClient 才能得到 Call 对象。只要掌握 http 请求的原理,使用起 okhttp 来也就不是什么问题了。首先 OkHttpClient 是用来设置关于请求工具的一些参数的,比如超时时间、是否缓存等等。Call 对象是发起 Http 请求的对象,通过 Call 对象来发起请求。
在调用一个耗时较长的接口时,我们往往需要显示一个加载框,以便让用户知道我们的 APP 正在工作而不是卡死。
一般的做法,是这样实现:
showLoading(); // 在请求开始之前显示加载框
http.async('/api/...')
.setOnComplete(state -> {
// 在请求结束(成功|失败|异常)之后关闭加载框
hideLoading();