Android 对象池的简单实现

首先简单说一下为什么要使用对象池,使用对象池的主要原因是防止短时间内频繁大量的创建和销毁对象,也就是频繁GC,引起内存抖动,导致影响我们应用的性能。

Android 对象 排序_java

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 自动清理对象池。