注释3: 检查ip地址和路由列表是否合法
注释4: 调用 RealCall.acquireConnectionNoEvents()
方法,将RealCall的 connection指向该连接,表明存在可以复用的连接,并且返回true。那么调用者就可以通过它的RealCall来获取到复用的连接了。
可以看下 RealCall的方法:
// RealCall.kt
fun acquireConnectionNoEvents(connection: RealConnection) {
connection.assertThreadHoldsLock()
check(this.connection == null)
this.connection = connection
connection.calls.add(CallReference(this, callStackTrace))
}
3.2.3 清除和回收连接
在刚刚put方法里面,我们看到了该类会实现一个方法来check连接池里的连接,它的作用是清除和回收超时和多出来的连接,我们来看看这个方法,因为方法比较长,所以分成两个部分来看,下面是上半部分:
// RealConnectionPool.kt
/**
-
作用是维护连接池,删除那些超时的连接、或者超出最大数量限制的连接
-
返回的值是睡眠到下次执行该方法的时间,
-
如果不需要进一步清理,则返回-1
*/
fun cleanup(now: Long): Long {
var inUseConnectionCount = 0
var idleConnectionCount = 0
var longestIdleConnection: RealConnection? = null
var longestIdleDurationNs = Long.MIN_VALUE
// 1
for (connection in connections) {
synchronized(connection) {
// 2
if (pruneAndGetAllocationCount(connection, now) > 0) {
inUseConnectionCount++
} else {
idleConnectionCount++
// 3
val idleDurationNs = now - connection.idleAtNs
if (idleDurationNs > longestIdleDurationNs) {
longestIdleDurationNs = idleDurationNs
longestIdleConnection = connection
} else {
Unit
}
}
}
}
…
}
注释1: 遍历连接池内的所有连接
注释2: 调用 pruneAndGetAllocationCount()
方法,查看该连接是否正在被使用。如果正在使用,则工作连接+1
,否则 闲置连接+1
注释3: 计算该连接的闲置时间。遍历一圈,记录下闲置时间最久的连接。
再来看下cleanup()
的下半部分:
// RealConnectionPool.kt
…
when {
// 1
longestIdleDurationNs >= this.keepAliveDurationNs
|| idleConnectionCount > this.maxIdleConnections -> {
val connection = longestIdleConnection!!
synchronized(connection) {
if (connection.calls.isNotEmpty()) return 0L // No longer idle.
if (connection.idleAtNs + longestIdleDurationNs != now) return 0L // No longer oldest.
connection.noNewExchanges = true
(longestIdleConnection)
}
connection.socket().closeQuietly()
if (connections.isEmpty()) cleanupQueue.cancelAll()
// Clean up again immediately.
return 0L
}
// 2
idleConnectionCount > 0 -> {
return keepAliveDurationNs - longestIdleDurationNs
}
// 3
inUseConnectionCount > 0 -> {
return keepAliveDurationNs
}
// 4
else -> {
return -1
}
}
这里是根据上半部分的统计结果进行处理:
注释1:闲置最久的连接时间已经超过5分钟或者当前空闲的连接数超过了5个,则通过 connections.remove()
和 connection.socket().closeQuietly()
移除掉闲置最久的连接,
注释2:当前存在闲置连接,则返回 闲置最久的连接还需要等待多少时间就到5分钟 的时间间隔
注释3:当前没有闲置连接,有工作连接, 则返回 5分钟
注释4:既没有工作连接又没有闲置连接,返回-1
这个方法主要就是通过计算有无超时的限制连接或则超过容量的连接进行删除,其中它使用了一个方法 pruneAndGetAllocationCount()
来查看一个连接是否正在被使用,我们可以看看这个方法的逻辑。
3.2.4 查看连接是否闲置
// RealConnectionPool.kt
/**
-
删除所有的发生泄漏的回调,然后返回[Connection]剩余的实时的被调用的数量
-
如果一个回调正在被引用但是实际上已经被代码不使用他们了,这个回调就是泄漏的,
-
这种泄漏检测是不靠谱的,而且依赖于 GC回收
*/
private fun pruneAndGetAllocationCount(connection: RealConnection, now: Long): Int {
connection.assertThreadHoldsLock()
// 1
val references = connection.calls
var i = 0
// 2
while (i < references.size) {
val reference = references[i]
// 3
if (reference.get() != null) {
i++
continue
}
// 4
val callReference = reference as CallReference
val message = "A connection to ${connection.route().address.url} was leaked. " +
“Did you forget to close a response body?”
Platform.get().logCloseableLeak(message, callReference.callStackTrace)
// 5
references.removeAt(i)
connection.noNewExchanges = true
if (references.isEmpty()) {
connection.idleAtNs = now - keepAliveDurationNs
return 0
}
}
return references.size
}
注释1:获取 connection的所有存储的 Reference<RealCall>
,也就是Call引用
注释2:遍历这些Call
注释3:如果Call通过 .get()
获取不为null,那么说明它正在被使用,则记录并continue,否则说明这个call已经被清除了,但是由于在列表中所有没有回收掉。
注释4: 抛出一个泄漏的Log,提醒开发者有没有遗漏close一个连接。(所以这里提示我们在使用完一个socket后,需要关闭到RealCall,否则我们就复用不了连接池)
注释5:Call列表移除掉这个泄漏的Call。
总的来说,pruneAndGetAllocationCount()
这个方法就是通过检查 Reference
来查看引用数,判断一个连接是否被call引用,如果引用,就说明这个连接是一个正在工作中的连接,否则就是一个闲置的连接。
我们已经知道 RealConnectionPool
、ConnectionPool()
是如何复用连接池了,那么我们来看看它是在什么时候运用在代码中的吧。
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
尾声
你不踏出去一步,永远不知道自己潜力有多大,千万别被这个社会套在我们身上的枷锁给捆住了,30岁我不怕,35岁我一样不怕,去做自己想做的事,为自己拼一把吧!不试试怎么知道你不行呢?
改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
你不踏出去一步,永远不知道自己潜力有多大,千万别被这个社会套在我们身上的枷锁给捆住了,30岁我不怕,35岁我一样不怕,去做自己想做的事,为自己拼一把吧!不试试怎么知道你不行呢?
改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-eUr0YkzX-1711544706215)]