前言
LeakCanary(https://square.github.io/leakcanary)是Square公司研发的一个可视化的内存泄漏分析工具,源码github地址为https://github.com/square/leakcanary。
LeakCanary对Android框架内部的了解使它有一种独特的能力来缩小每次泄露的原因,帮助开发人员显著减少OutOfMemoryError崩溃。
LeakCanary运作原理
第一步:检查保留的对象
LeakCanary可嫁接到Android的生命周期中,以便于自动检查activity 和Fragment等销毁时进行垃圾收集,这些被销毁的对象的若应用被传递给ObjectWatcher对象。支持自动检查的对象有:
被销毁的Activity实例、被销毁的Fragment实例、被销毁的Fragment View实例,被清除的ViewModel实例
如果这些被ObjectWatcher所持有的对象的弱应用在执行GC 5秒后,ObjectWather仍然持有对象的弱应用,那该对象就被视为内存泄漏。
LeakCanary等待持有的这些泄漏对象数量达到一个阈值(5)的时候,就会将这些对象dump到转储堆中;而在未达到阈值的时候,会发出一个找到几个泄漏对象的通知。
第二步:转储堆
LeakCanary将这些保留对象转储到Android文件系统的.hprof文件中,这回导致程序冻结一小段时间,同时也会发出正在dump的通知“LeakCanary is dumping the memory to investigate leaks”。
第三步:分析堆
LeakCanary使用Shrk来解析上一步的.hproof文件,找到保留的对象。并从垃圾回收的泄漏跟踪中找到该保留对象的引用路径。
LeakCanary会为每一个泄漏跟踪创建一个签名,将签名相同的泄漏组合在一起,然后将以通知的方式显示分析结果的摘要,并在Logcat中打印结果。
点击通知,可以打开每一条泄漏的位置及其详细引用关系。
第四步:泄漏分类
将发现的泄漏分为两类: 应用程序泄漏和库泄漏,关于库的泄漏我们就可以飘过了,我们主要关心应用程序的泄漏就好了。
LeakCanary使用
第一步: 准备阶段:
1)自定义LeakCanaryApplication
class LeakCanaryApplication : Application() {
//用于存放视图
val viewMap = ArrayList<View>()
override fun onCreate(){
super.onCreate();
}
}
2) MainActivity中添加一个TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.helloworld);
//将布局加载出来的视图添加到application的视图容器中
(application as LeakCanaryApplication).viewMap.add(textView)
}
}
第二步:build.gradle中添加依赖 & (可选)自定义Application的onCreate()配置
//leakCanary for debug
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.6'
// NEW: LeakCanary for releases!
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-release:2.6'
// Optional: detect retained objects. This helps but is not required.
releaseImplementation 'com.squareup.leakcanary:leakcanary-object-watcher-android:2.6'
由于笔者用的是最新的LeakCanary 2.6的版本,已经完全支持Kotlin ,而且2.6 添加依赖后不需要在application中初始化了。不过如果你觉得默认的不够白富美,可以自己在application中进行一些配置(当然不止下面的那些):
override fun onCreate() {
super.onCreate()
//要自定义堆转储和分析,
LeakCanary.config = LeakCanary.config.copy(retainedVisibleThreshold = 3)
//要在运行时自定义对保留对象的检测
AppWatcher.config=AppWatcher.config.copy(watchFragmentViews = false)
//禁用堆转储和分析 AppWatcher.objectWatcher仍然会保持跟踪保留的对象,并LeakCanary将寻找这些对象当您更改LeakCanary.Config.dumpHeap回true。
LeakCanary.config = LeakCanary.config.copy(dumpHeap = false)
//隐藏泄漏显示活动启动器图标
LeakCanary.showLeakDisplayActivityLauncherIcon(false)
}
好了就这么简单,你现在可以大摇大摆的运行你的app了。
因为LeakCanary检查的是activity ,fragment在调用onDestory时发生GC后5秒仍然还保留的对象,所以我们点击设备的Back按键让页面关闭,看看日志:
这时候在我们的运行该app的设备上会收到一条通知:
点击上图中的通知,会自动开始dump,如下图
dump会将签名相同的泄漏进行合并,我们继续点击该通知:如下
打开通知后,就是LeakCanary抓取的泄漏的地方,继续点击进去
下图就是LeakCanary 检查出来泄漏的地方(左侧红色线和下划线标注),是不是很清晰
LeakCanary 可以帮我们找到内存泄漏的地方对象及其引用关系,接下来就需要我们自己去堵上这个泄漏了。笔者就不在废话了.
Good Luck !!!!!!!