Android内存泄漏检测

/ 0评 / 0

前言

https://27house.cn/archives/2396中已经介绍了,使用WeakReference可以与ReferenceQueue来判断是否执行了GC。按照这个理论,当GC的时候,就可以检测一下Activity是否泄露!

实现

代码如下所示,其中GcMonitor的代码在https://27house.cn/archives/2396中,这样就可以将泄露信息做自定义处理或者dump上报

data class UILeakMonitorConfig(
    val checkInterval: Long = 1000 * 60,
    val leakProcessor: ((leakActivitys: List<DestroyedActivityInfo>) -> Unit)? = null
)

object UILeakMonitor {

    private const val TAG = "UILeakMonitor"
    private var gcMonitor: GcMonitor? = null
    private var config = UILeakMonitorConfig()

    private val waitGcActivitys = mutableListOf<DestroyedActivityInfo>()

    fun start(app: Application, config: UILeakMonitorConfig = UILeakMonitorConfig()) {
        this.config = config
        app.registerActivityLifecycleCallbacks(object : SimpleActivityLifecycleCallbacks() {
            override fun onActivityDestroyed(activity: Activity) {
                initGcMonitor()
                pushDestroyedActivityInfo(activity)
            }
        })
    }

    private fun pushDestroyedActivityInfo(activity: Activity) {
        val activityInfo = DestroyedActivityInfo(
            key = generateKey(),
            activityName = activity::class.java.name,
            activityRef = WeakReference(activity),
            activityDestroyMs = System.currentTimeMillis()
        )
        waitGcActivitys.add(activityInfo)
    }

    private fun initGcMonitor() {
        if (gcMonitor == null) {
            gcMonitor = GcMonitor()
            gcMonitor?.startMonitor {
                Log.i(TAG, "触发了GC")
                Handler(Looper.getMainLooper()).post {
                    gc()
                }
            }
        }
    }

    private fun gc() {
        if (Debug.isDebuggerConnected()) {
            return
        }
        val iterator = waitGcActivitys.iterator()
        val leakedActivitys = mutableListOf<DestroyedActivityInfo>()
        while (iterator.hasNext()) {
            val destroyedActivityInfo = iterator.next()
            if (destroyedActivityInfo.activityRef?.get() == null) {
                iterator.remove()
                Log.i(TAG, "${destroyedActivityInfo.activityName} 已经被回收")
                continue
            }
            if (System.currentTimeMillis() - destroyedActivityInfo.activityDestroyMs < config.checkInterval) {
                Log.i(
                    TAG,
                    "${destroyedActivityInfo.activityName} 小于${config.checkInterval},不作为泄露"
                )
                continue
            }
            leakedActivitys.add(destroyedActivityInfo)
        }
        config.leakProcessor?.invoke(leakedActivitys)
    }

    private fun generateKey(): String {
        return UUID.randomUUID().toString()
    }
}
data class DestroyedActivityInfo(
    val key: String,
    val activityName: String,
    val activityRef: WeakReference<Activity>? = null,
    val activityDestroyMs: Long
)

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注