一、Activity 生命周期回调方法 LifecycleCallbacks
在 Application
中注册 ActivityLifecycleCallbacks
时,可以监听到每个 Activity
的生命周期方法的执行。当 Activity
被销毁时,会回调到 onActivityDestroyed
方法,此时便是 LeakCanary
开始检测 Activity
是否泄露的时机。LeakCanary 通过 RefWatcher.watch(activity)
方法,判断刚刚调用了 onDestroy
方法的 Activity
是否泄露了。
二、弱引用 - WeakReference
WeakReference
继承自 Reference
,其构造函数如下所示,每个 WeakReference
都会关联一个 ReferenceQueue
,当 WeakReference
所指向的对象被 GC 后,这个 WeakReference
对象都会被放入与之相关联的 ReferenceQueue
中。
public abstract class Reference<T> {
final ReferenceQueue<? super T> queue;
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = queue;
}
}
监控 Activity 是否泄露的逻辑
在 Activity
调用 onDestroy
方法后,理论上,接下来他应该被回收,这时指向 Activity
的弱引用应该被放入 ReferenceQueue
队列中,这时 ReferenceQueue
不应该为空,如果为空,说明 Activity
还没有被回收,发生了泄露。为了避免由于 gc 不及时带来的误判,LeakCanary 会进行一次手动 gc,代码如下所示
com/squareup/leakcanary/RefWatcher.java
// 先清空 ReferenceQueue
removeWeaklyReachableReferences();
// 如果此时 activity 不可达了,说明已经被成功回收,没有泄露
if (gone(reference) || debuggerControl.isDebuggerAttached()) {
return;
}
// 手动 gc
gcTrigger.runGc();
// 再次清空 ReferenceQueue
removeWeaklyReachableReferences();
// 如果此时 activity 仍然可达,说明发生了 泄露,接下来会导出 heapDump 文件进行分析
if (!gone(reference)) {
...
}
判断是否泄露的时机
IdleHandler
会在 UI 线程的 MessageQueue
为空时调用,由下面代码可知,会在主线程空闲时,5秒后派发一个判断是否泄露的任务 给 后台线程。
public final class AndroidWatchExecutor implements Executor {
private static final int DELAY_MILLIS = 5000;
//....
private void executeDelayedAfterIdleUnsafe(final Runnable runnable) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
backgroundHandler.postDelayed(runnable, DELAY_MILLIS);
return false;
}
});
}
}
总结
LeakCanary 在 Activity
执行 onDestroy
后,利用 WeakReference
指向的对象被 GC 回收后会放入 ReferenceQueue
的特性,在后台线程中检测 Activity
是否泄露。当检测到泄露时,通过 AndroidHeapDumper
的 dumpHeap
方法导出应用的栈信息到本地磁盘,然后在运行在私有进程:leakcanary
下的 HeapAnalyzerService
组件中对栈信息进行分析,找到泄露的引用和调用轨迹,最后把分析结果发送到通知栏,告知开发者存在内存泄漏。