问题2:如何实现自动监听Activity的内存泄露?
让我们回到上篇的最后,我们知道了ActivityDestroyWatcher中的install中,
注册了appcation的activity生命周期变化的监听。
监听的实现如上图。
其实就是在onActivityDestroyed的方法中,使用ObjectWatch类watch了执行到onDestroyed的activity。
ObjectWatcher是干什么的?
/**
* [ObjectWatcher] can be passed objects to [watch]. It will create [KeyedWeakReference] instances
* that reference watches objects, and check if those references have been cleared as expected on
* the [checkRetainedExecutor] executor. If not, these objects are considered retained and
* [ObjectWatcher] will then notify the [onObjectRetainedListener] on that executor thread.
*
* [checkRetainedExecutor] is expected to run its tasks on a background thread, with a significant
* to give the GC the opportunity to identify weakly reachable objects.
*
* [ObjectWatcher] is thread safe.
*/
在这里我把源码中的注释翻译了一下,官方的解释比较贴切。
ObjectWatch可以监视传入的对象,它会创建监视传入对象的弱引用实例,检查这些引用是否已经背checkRetainedExecutor执行者清除。如果没有,这些对象被考虑保留,然后ObjectWatcher将会在那个执行者的线程里通知onObjectRetainedListener。
checkRetainedExecutor被期望用来在后台线程运行的任务,带上一个象征,使GC有机会识别弱可及对象。
checkRetainedExecutor,是Executor的一个实现。
ObjectWatcher如何检测传入的Object的回收状态?
让我们先看看ObjectWatch的watch方法
1,removeWeaklyReachableObjects
将watcher中存储的所有未被clear的WeakReference引用入队。
2,为传入的待监视的对象创建弱引用实例。
3,交给checkRetainedExecutor去执行。
让我们继续看看moveToRetained的方法中做了什么
如果等到handler之前发送的消息开始执行的时候,也会先进行一次检查,将watcher中存储的所有未被clear的WeakReference引用入队。
如果还有弱引用还未被回收,这个时候ObjectWatcher会通知外部有对象被保留了。
onOjectRetainedListeners是在哪里注册的呢?
ObjectWatcher提供了外部设置监听的方法,往上查找调用者,发现只有InternalLeakCanary的invoke方法进行了调用。
AppWatcher.objectWatcher实际上就是InternalAppWatcher中创建的watcher,也就是传入ActivityDestroyWatcher的ObjectWatcher
/**
* The [ObjectWatcher] used by AppWatcher to detect retained objects.
*/
val objectWatcher
get() = InternalAppWatcher.objectWatcher
/** @see [manualInstall] */
val isInstalled
get() = InternalAppWatcher.isInstalled
ok,现在我们知道InternalLeakCanary类中实现了OnRetainListener,我们看看它实现的方法
override fun onObjectRetained() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.onObjectRetained()
}
}
在方法中最终交由HeapDumpTrigger来进行管理。
HeapDumpTrigger
让我们继续看HeapDumpTrigger的onObjectRetained方法做了什么
fun onObjectRetained() {
scheduleRetainedObjectCheck(
reason = "found new object retained",
rescheduling = false
)
}
private fun scheduleRetainedObjectCheck(
reason: String,
rescheduling: Boolean,
delayMillis: Long = 0L
) {
val checkCurrentlyScheduledAt = checkScheduledAt
if (checkCurrentlyScheduledAt > 0) {
val scheduledIn = checkCurrentlyScheduledAt - SystemClock.uptimeMillis()
SharkLog.d { "Ignoring request to check for retained objects ($reason), already scheduled in ${scheduledIn}ms" }
return
} else {
val verb = if (rescheduling) "Rescheduling" else "Scheduling"
val delay = if (delayMillis > 0) " in ${delayMillis}ms" else ""
SharkLog.d { "$verb check for retained objects${delay} because $reason" }
}
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects(reason)
}, delayMillis)
}
关键看最后一行
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects(reason)
}, delayMillis)
往后台线程发送了一个消息。
checkRetainedObjects方法中就是检测对象回收状态、提示、存储的主要实现了。
private fun checkRetainedObjects(reason: String) {
val config = configProvider()
// A tick will be rescheduled when this is turned back on.
if (!config.dumpHeap) {
SharkLog.d { "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false" }
return
}
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
// 如果保留的对象数量大于0,则立刻进行一次gc
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
// 再进行一次检查,如果通过则直接返回
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
//如果不允许dump堆内存,并且处于调试状态,则进行提示,并且安排下一次的检查,直接return
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(
R.string.leak_canary_notification_retained_debugger_attached
)
)
scheduleRetainedObjectCheck(
reason = "debugger is attached",
rescheduling = true,
delayMillis = WAIT_FOR_DEBUG_MILLIS
)
return
}
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
//如果未过dump内存的间隔,则只弹出通知栏提示,并且安排下一次检查,直接return
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
)
scheduleRetainedObjectCheck(
reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
rescheduling = true,
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
}
//dump内存。
SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" }
dismissRetainedCountNotification()
dumpHeap(retainedReferenceCount, retry = true)
}