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 自動清理物件池,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/87727.html
標籤:其他
