LeakCanary工作流程
什么时候开始检测泄漏
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
//初始化RefWatcher,RefWatcher主要是检测是否内存泄漏并且获取相关的hprof文件
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
if (enableDisplayLeakActivity) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
}
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
在初始化完RefWatcher之后 调用ActivityRefWatcher.install(context, refWatcher)监听activity的ondestory,调用FragmentRefWatcher.Helper.install(context, refWatcher)监听fragment的onFragmentViewDestroyed和onFragmentDestroyed。
activity通过application.registerActivityLifecycleCallbacks()获取activity的ondestory事件。
fragment通过supportFragmentManager.registerFragmentLifecycleCallbacks获取fragment回调事件,该方法只在supportFragmentManager 和8.0以上的FragmentManager才有,所以8.0以下的android.app.Fragment是无法监听到泄漏的。
怎么检测泄漏
当知道activity或者fragment销毁之后。调用RefWatcher.watch(Object watchedReference),通过检测传入的引入是否被回收来判断内存是否泄漏
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
//检测对象是否是可回收的或者被回收
removeWeaklyReachableReferences();
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
//是可回收的 就返回 表示没发生泄漏
if (gone(reference)) {
return DONE;
}
//调用gc
gcTrigger.runGc();
//再次检测
removeWeaklyReachableReferences();
//还是不可回收
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
//获取hprof
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
//封装
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
//开始分析
heapdumpListener.analyze(heapDump);
}
return DONE;
}
这里将检测的对象设置为弱引用,并且设置引用对列ReferenceQueue。
这样当弱引用对象被标记为finalizable,表示可以被回收,这时垃圾收集器就会把对象的弱引用放入ReferenceQueue队列中。
而检测对象是否可回收的方法
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
就是通过判断队列是否有相应的key来实现的。
怎么分析hprof文件
通过ServiceHeapDumpListener.analyze(@NonNull HeapDump heapDump)最终调用HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
HeapAnalyzerService 是个前台服务 主要分析hprof文件并将相关数据放入AnalysisResult
@Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
//具体使用[haha](https://github.com/square/haha)分析hprof文件
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
//把heapDump 和heapAnalyzer 存储在本地,以xx.result文件结尾
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
AbstractAnalysisResultService也是一个前台服务 ,他的工作是:
读取刚刚存储的xx.result文件,以当前时间为名字重命名,方便按照时间排序显示。
获取HeapDump和AnalysisResult里面重要的文字 显示在通知栏。
当点击通知栏的时候调用DisplayLeakActivity显示具体的信息。