Android 对象池的简单实现
首先简单说一下为什么要使用对象池,使用对象池的主要原因是防止短时间内频繁大量的创建和销毁对象,也就是频繁GC,引起内存抖动,导致影响我们应用的性能。
package com.qumitech.common.pool
/**
* author : hnliu
* e-mail : 1017928908@qq.com
* create date : 2020/09/12
* description :管理对象池的接口
*/
interface Pool<T> {
// 返回一个对象实例,如果对象池里面有则从里面直接取一个,没有则创建一个对象
fun get(): T?
// 释放一个实例到对象池
fun recycle(instance: T): Boolean
// 清空对象池
fun clear()
}
简单对象池,实现了从池子里面取一个对象出来,往对象池里面添加对象,清除对象池等简单操作
package com.qumitech.common.pool
import android.os.Handler
import android.os.Looper.getMainLooper
import android.util.Log
import com.qumitech.common.xlog.XLogExt
/**
* author : hnliu
* e-mail : 1017928908@qq.com
* create date : 2020/09/12
* description : 简单的对象池
*/
open class SimplePool<T>(maxPoolSize: Int) : Pool<T> {
class PoolObj<T>(val obj: T, var startTime: Long?)
val mPool: Array<PoolObj<T>?>
var mPoolSize = 0
override fun get(): T? {
if (mPoolSize > 0) {
val lastPooledIndex = mPoolSize - 1
val instance = mPool[lastPooledIndex]?.obj
mPool[lastPooledIndex] = null
Log.i("SimplePool", "从对象池获取一个对象:${instance}, 当前对象数量:${mPoolSize}")
mPoolSize--
return instance
}
return null
}
override fun recycle(instance: T): Boolean {
check(!isInPool(instance)) { "Already in the pool!" }
if (mPoolSize < mPool.size) {
mPool[mPoolSize] = PoolObj(instance, System.currentTimeMillis())
mPoolSize++
Log.i("SimplePool", "回收一个对象:${instance}, 当前对象数量:${mPoolSize}")
return true
}
return false
}
override fun clear() {
for(i in mPool.indices) {
mPool[i] = null
}
}
private fun isInPool(instance: T): Boolean {
for (i in 0 until mPoolSize) {
if (mPool[i] === instance) {
return true
}
}
return false
}
init {
require(maxPoolSize > 0) { "The max pool size must be > 0" }
mPool = arrayOfNulls(maxPoolSize)
Log.i("SimplePool", "创建了一个简单对象池")
}
}
自动释放对象池,在简单对象池的基础上,扩展了自动释放对象池的功能。目前这一版的设计,释放操作是在主线程执行。考虑到新开一个线程进行释放操作,首先会带来线程数量限制的额外负担,其次需要对对象池加锁,带来性能上的消耗。
package com.qumitech.common.pool
import android.os.Handler
import android.os.Looper.getMainLooper
import android.util.Log
import com.qumitech.common.xlog.XLogExt
/**
* author : hnliu
* e-mail : 1017928908@qq.com
* create date : 2020/09/12
* description : 可以自动释放的对象池
*/
open class AutoReleasePool<T>(maxPoolSize: Int, private val keyAliveSize: Int, private val timeOutLimit: Long, private val checkInterval: Long, val onEmpty: () -> Unit) : SimplePool<T>(maxPoolSize) {
private var running = false
private val handler: Handler by lazy {
Handler(getMainLooper())
}
private val runnable: Runnable by lazy {
Runnable {
if(running) {
clearNotUsedForLongTime()
handler.postDelayed(runnable, checkInterval)
}
}
}
override fun recycle(instance: T): Boolean {
//回收一个元素以后,需要开启定时器
if(super.recycle(instance) && !running) {
handler.postDelayed(runnable, checkInterval)
running = true
}
return false
}
private fun getLongTimeNotUsedObjectCount(): Int {
var result = 0
for(i in 0 until mPoolSize) {
val poolObj = mPool[i]
val time = System.currentTimeMillis().minus(poolObj?.startTime?:0)
if(time > timeOutLimit) {
result++
}
}
return result
}
private fun clearNotUsedForLongTime() {
val start = System.currentTimeMillis()
//如果1/3对象已经10秒没有使用,则清理1/3的对象
if(getLongTimeNotUsedObjectCount() * 3 >= mPoolSize) {
var count = if(mPoolSize > 3) (mPoolSize / 3) else mPoolSize
while(count-- > 0 && mPoolSize > keyAliveSize) {
val lastPooledIndex = mPoolSize - 1
mPool[lastPooledIndex] = null
mPoolSize--
}
Log.i("AutoReleasePool", "${this.hashCode()}清理1/3长时间未使用的对象, 清理完还剩:${mPoolSize}")
//如果都清理完了,则关闭清理定时器
if(running && mPoolSize == 0) {
handler.removeCallbacks(runnable)
running = false
onEmpty()
}
}
Log.i("AutoReleasePool", "本次清理共耗时:${System.currentTimeMillis() - start}毫秒")
}
init {
Log.i("AutoReleasePool", "创建了一个自动清理对象池:${this.hashCode()}")
}
}
同步对象池,对池子的获取和归还操作进行了加锁
package com.qumitech.common.pool
/**
* author : hnliu
* e-mail : 1017928908@qq.com
* create date : 2020/09/12
* description : 同步的对象池
*/
class SynchronizedPool<T>(maxPoolSize: Int, autoRelease: Boolean) : SimplePool<T>(maxPoolSize) {
private val mLock = Any()
override fun get(): T? {
synchronized(mLock) { return super.get() }
}
override fun recycle(instance: T): Boolean {
synchronized(mLock) { return super.recycle(instance) }
}
}
package com.qumitech.common.pool.widget
import android.content.Context
import android.support.v7.widget.AppCompatImageView
import android.util.AttributeSet
import android.widget.ImageView
import com.qumitech.common.pool.AutoReleasePool
import com.qumitech.common.pool.Pool
/**
* author : hnliu
* e-mail : 1017928908@qq.com
* create date : 2020/09/14
* description : 实现了对象池的ImageView,每次创建的时候从对象池里取,使用完毕归还对象池
*/
class RecycleImageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatImageView(context, attrs, defStyleAttr) {
companion object {
//每个池子存储的最大数量
private const val MAX_POOL_SIZE = 64
//每一种礼物都分配一个池子,池子里面最多装64个对象,多于64个,回收的时候,不在放入池子里面
@JvmStatic
private val sPool: Pool<RecycleImageView?>? by lazy { AutoReleasePool<RecycleImageView?>(MAX_POOL_SIZE, 10,10 * 1000L, 10 * 1000L, {}) }
@JvmStatic
fun create(context: Context): ImageView {
val instance = sPool?.get()
return instance?:RecycleImageView(context)
}
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
sPool?.recycle(this)
}
}
每次创建30000个内存占用2K大小的对象的时间对比
不使用对象池的情况下:
2020-09-16 18:05:26.526 16541-16541/com.czy.pooltest I/MainActivity: cost time1:275
2020-09-16 18:05:26.990 16541-16541/com.czy.pooltest I/MainActivity: cost time1:160
2020-09-16 18:05:27.506 16541-16541/com.czy.pooltest I/MainActivity: cost time1:309
2020-09-16 18:06:01.788 16541-16541/com.czy.pooltest I/MainActivity: cost time1:204
2020-09-16 18:06:01.976 16541-16541/com.czy.pooltest I/MainActivity: cost time1:182
2020-09-16 18:06:02.168 16541-16541/com.czy.pooltest I/MainActivity: cost time1:188
2020-09-16 18:06:02.526 16541-16541/com.czy.pooltest I/MainActivity: cost time1:178
2020-09-16 18:06:02.732 16541-16541/com.czy.pooltest I/MainActivity: cost time1:203
2020-09-16 18:06:02.996 16541-16541/com.czy.pooltest I/MainActivity: cost time1:261
使用了 SimplePool 对象池的情况,性能提升将近10-20倍
2020-09-16 18:06:06.588 16541-16541/com.czy.pooltest I/MainActivity: cost time2:14
2020-09-16 18:06:11.943 16541-16541/com.czy.pooltest I/MainActivity: cost time2:15
2020-09-16 18:06:17.196 16541-16541/com.czy.pooltest I/MainActivity: cost time2:16
2020-09-16 18:05:29.887 16541-16541/com.czy.pooltest I/MainActivity: cost time2:23
2020-09-16 18:05:32.399 16541-16541/com.czy.pooltest I/MainActivity: cost time2:18
2020-09-16 18:05:37.072 16541-16541/com.czy.pooltest I/MainActivity: cost time2:15
2020-09-16 18:05:42.252 16541-16541/com.czy.pooltest I/MainActivity: cost time2:55
2020-09-16 18:05:47.002 16541-16541/com.czy.pooltest I/MainActivity: cost time2:18
2020-09-16 18:05:52.004 16541-16541/com.czy.pooltest I/MainActivity: cost time2:16
2020-09-16 18:05:57.009 16541-16541/com.czy.pooltest I/MainActivity: cost time2:15
使用了 AutoReleasePool 对象池的情况,性能提升将近10-20倍
2020-09-16 18:11:44.932 17289-17289/com.czy.pooltest I/MainActivity: cost time2:19
2020-09-16 18:11:50.644 17289-17289/com.czy.pooltest I/MainActivity: cost time2:16
2020-09-16 18:11:54.169 17289-17289/com.czy.pooltest I/MainActivity: cost time2:56
2020-09-16 18:12:01.718 17289-17289/com.czy.pooltest I/MainActivity: cost time2:18
2020-09-16 18:12:04.411 17289-17289/com.czy.pooltest I/MainActivity: cost time2:56
2020-09-16 18:12:09.208 17289-17289/com.czy.pooltest I/MainActivity: cost time2:17
2020-09-16 18:12:44.964 17448-17448/com.czy.pooltest I/MainActivity: cost time2:20
2020-09-16 18:12:50.742 17448-17448/com.czy.pooltest I/MainActivity: cost time2:17
2020-09-16 18:12:57.122 17448-17448/com.czy.pooltest I/MainActivity: cost time2:15
2020-09-16 18:13:02.046 17448-17448/com.czy.pooltest I/MainActivity: cost time2:18
线程池清理逻辑消耗时间
2020-09-16 18:13:18.774 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:20000
2020-09-16 18:13:18.774 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:136毫秒
2020-09-16 18:13:18.811 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:13334
2020-09-16 18:13:18.811 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:37毫秒
2020-09-16 18:13:28.849 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:8890
2020-09-16 18:13:28.849 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:64毫秒
2020-09-16 18:13:28.880 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:5927
2020-09-16 18:13:28.880 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:31毫秒
2020-09-16 18:13:38.884 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:3952
2020-09-16 18:13:38.884 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:29毫秒
2020-09-16 18:13:38.903 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:2635
2020-09-16 18:13:38.903 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:19毫秒
2020-09-16 18:13:48.907 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:1757
2020-09-16 18:13:48.907 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:13毫秒
2020-09-16 18:13:48.916 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:1172
2020-09-16 18:13:48.916 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:9毫秒
2020-09-16 18:13:58.921 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:782
2020-09-16 18:13:58.921 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:6毫秒
2020-09-16 18:13:58.925 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:522
2020-09-16 18:13:58.925 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:4毫秒
2020-09-16 18:14:08.934 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:348
2020-09-16 18:14:08.934 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:3毫秒
2020-09-16 18:14:08.936 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:232
2020-09-16 18:14:08.936 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:2毫秒
2020-09-16 18:14:18.946 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:155
2020-09-16 18:14:18.946 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:1毫秒
2020-09-16 18:14:18.947 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:104
2020-09-16 18:14:18.947 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:1毫秒
2020-09-16 18:14:28.958 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:70
2020-09-16 18:14:28.958 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:1毫秒
2020-09-16 18:14:28.959 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:47
2020-09-16 18:14:28.959 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:1毫秒
2020-09-16 18:14:38.964 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:32
2020-09-16 18:14:38.964 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:1毫秒
2020-09-16 18:14:38.964 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:22
2020-09-16 18:14:38.964 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:0毫秒
2020-09-16 18:14:48.967 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:15
2020-09-16 18:14:48.967 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:1毫秒
2020-09-16 18:14:48.968 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:10
2020-09-16 18:14:48.968 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:1毫秒
2020-09-16 18:14:58.975 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:7
2020-09-16 18:14:58.975 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:0毫秒
2020-09-16 18:14:58.976 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:5
2020-09-16 18:14:58.976 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:0毫秒
2020-09-16 18:15:08.987 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:4
2020-09-16 18:15:08.987 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:0毫秒
2020-09-16 18:15:08.987 17448-17448/com.czy.pooltest I/AutoReleasePool: 134718435清理1/3长时间未使用的对象, 清理完还剩:3
2020-09-16 18:15:08.988 17448-17448/com.czy.pooltest I/AutoReleasePool: 本次清理共耗时:0毫秒
得出的结论是当对象数目低于500以下时,在主线程做清理处理,基本不会卡顿UI, 不期望新开线程去做清理工作。如果所需的对象池很大,则使用 SimplePool 手动进行清理, 不推荐使用 AutoReleasePool 自动清理对象池。