前言
上篇文章分析了 LeakSentry 的源码,本文在此基础上来分析下 LeakCanary 的核心库——Leakcanary-android的源码。
原理
LeakCanary 的原理为当 Activity、Fragment 销毁的时候会调用 RefWatcher.watch 来观察这些对象,如果这些对象得不到释放那么就会一直保存在 retainedReferences 中,当 App 切到后台一定时间后或者保留的引用超过了临界值,那么就会手动的触发一个 GC,如果触发后条件仍然满足,那么就会去生成内存堆转储快照,然后开启服务去分析该文件,找出泄露的对象,并给出引用链。
源码分析
首先根据上篇文章我们知道在 InternalLeakSentry.init 代码块中会将 InternalLeakCanary 实例作为 LeakSentry 的监听器,在 InternalLeakSentry.install 的最后回调了InternalLeakCanary.onLeakSentryInstalled。
InternalLeakCanary.onLeakSentryInstalled
代码如下:
override fun onLeakSentryInstalled(application: Application) { |
方法内部主要构造了一个 HeapDumpTrigger 实例,然后调用了 registerVisibilityListener 这个扩展方法。
internal fun Application.registerVisibilityListener(listener: (Boolean) -> Unit) { |
方法内部通过监听 Activity 声明周期,每次 onStart 的时候将可见 Activity 数量加1,如果可见数量为 1,并且原来是不可见的,那么就认为 App 从不可见转变到了可见。每次 onStop 的时候将可见 Activity 数量减1,如果可见数量变成了 0,并且原来是可见的,并且不是因为配置改变导致的(配置改变会立马又再次启动该 Activity),那么就认为 App 从可见转变到了不可见。再看 onLeakSentryInstalled,在可见状态发生变化后会调用 onApplicationVisibilityChanged。
heapDumpTrigger.onApplicationVisibilityChanged
代码如下:
fun onApplicationVisibilityChanged(applicationVisible: Boolean) { |
主要看看从可见变成不可见时的情况(App被切换到后台),记录下应用程序开始不可见的时间。
private fun scheduleRetainedInstanceCheck(reason: String, delayMillis: Long) { |
在5秒(默认)后,会在子线程中执行 checkRetainedInstances 。
heapDumpTrigger.checkRetainedInstances
代码如下:
private fun checkRetainedInstances(reason: String) { |
主要来看下 heapDumper.dumpHeap 和 HeapAnalyzerService.runAnalysis,heapDumper 是一个AndroidHeapDumper 实例。
override fun dumpHeap(): File? { |
成功生成堆转储文件后会调用 HeapAnalyzerService.runAnalysis 进行分析
internal class HeapAnalyzerService : ForegroundService(...), AnalyzerProgressListener { |
可以看到主要的逻辑都在 heapAnalyzer.checkForLeaks 中了。
fun checkForLeaks(*): HeapAnalysis { |
当分析堆转储文件完毕后会回到 onHandleIntentInForeground 继续执行 analysisResultListener。
LeakCanary.config.analysisResultListener
代码如下:
object DefaultAnalysisResultListener : AnalysisResultListener { |
方法内部主要就是保存数据到数据库,显示一个 Notification,点击后跳转到 LeakActivity 显示 Screens 的内容。接着上篇文章最后还说到了当 RefWatcher 内部往 retainedReferences 内部添加一个对象时都会回调 onReferenceRetained,这做的事情跟可见度从可见变成不可见的事情一样。
总结
LeakSentry 默认使用 RefWatcher 观察将要被销毁的 Activity、Fragment(用户也可自定义观察对象),在被观察5秒后如果对象还没被回收,并且保留的对象数量大于临界值或者应用不可见超过5秒那么就会手动运行一下 GC,运行后如果还满足那么会生成内存堆转储快照,然后开启一个前台服务( IntentService )去解析该快照,然后将解析结果与原先被观察但还没回收的对象进行对比,找出真正泄露的对象,比给出最短引用链,当 App 从可见到不可见时也同样会触发该逻辑。