前言

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()

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(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按键让页面关闭,看看日志:

8a6edf360133

发现泄漏logcat日志.png

这时候在我们的运行该app的设备上会收到一条通知:

8a6edf360133

发现泄漏的通知.png

点击上图中的通知,会自动开始dump,如下图

8a6edf360133

发现泄漏的通知2.png

dump会将签名相同的泄漏进行合并,我们继续点击该通知:如下

8a6edf360133

发现泄漏的通知3.png

打开通知后,就是LeakCanary抓取的泄漏的地方,继续点击进去

8a6edf360133

发现泄漏的通知4.png

下图就是LeakCanary 检查出来泄漏的地方(左侧红色线和下划线标注),是不是很清晰

8a6edf360133

发现泄漏的通知5.png

LeakCanary 可以帮我们找到内存泄漏的地方对象及其引用关系,接下来就需要我们自己去堵上这个泄漏了。笔者就不在废话了.

Good Luck !!!!!!!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐