leakcanary2.0的学习与使用
[TOC]
概述
源码参考:https://github.com/square/leakcanary
官网地址:https://square.github.io/leakcanary/
我们先看一下使用方法:
添加依赖
1 | // 1.0的最新版本 |
2.0版本的使用方法
1 | // 现在最新的版本是2.4的版本 |
2.0与1.0的区别与联系
配合Gradle3.0引入的debugImplementation我们不需要添加no-op包来分离测试和生产环境了.
LeakCanary也不需要再单独的进程里来分析了,2.0之前的版本AS里可以看到LeakCanary是单独开了一个进程的
2.0不再需要我们手动执行初始化了, 我们看看2.0自动注册的实现.
2.0的使用更加简单方便
LeakCanary2.0原理
LeakCanary的原理很简单: 在Activity或Fragment被销毁后, 将他们的引用包装成一个WeakReference
, 然后将这个WeakReference
关联到一个ReferenceQueue
.查看ReferenceQueue
中是否含有Activity或Fragment的引用, 如果没有触发GC,再次查看,还是没有的话就说明没有回收成功, 可能发生了泄露. 这时候开始dump内存的信息,并分析泄露的引用链.
LeakCanary主要是通过4个步骤自动检测并报告内存泄漏:
1、检查残留对象
2、Dumping堆内存
3、堆转储文件的分析
4、进行内存泄漏的分析
下面,我们来依次说一下这几个工作步骤:
1、检查残留对象
LeakCanary是hook了Android的生命周期,然后来自动检测Activity和Fragment何时被销毁并应该被垃圾收集器回收。
这些应该被销毁的对象将传递给ObjectWatcher,该对象持有对它们的弱引用。
LeakCanary自动检测以下对象的泄漏:
- destroyed Activity instances
- destroyed Fragment instances
- destroyed fragment View instances
- cleared ViewModel instances
当然,您也可以自定义查看任何需要被回收对象,例如已经detached的View或者使用完毕的presenter
1 | AppWatcher.objectWatcher.watch(myDetachedView, "View was detached") |
如果等待5秒钟并运行垃圾收集后,仍未清除ObjectWatcher持有的弱引用,则该被监视对象被视为retained,并且有可能泄漏。 LeakCanary将此记录到Logcat:
1 | D LeakCanary: Watching instance of com.example.leakcanary.MainActivity |
LeakCanary在转储堆之前等待保留对象的数量达到阈值,并显示具有最新数量的通知。
2、Dumping the heap进行堆转储
当保留对象的数量达到一个阈值时,LeakCanary将Java堆转储到一个. hprof文件(一个堆转储)中,并存储到安卓文件系统中(参见LeakCanary在哪里存储堆转储?(https://square.github.io/leakcanary/faq/#where-does-leakcanary-store-heap-dumps))。转储堆会在短时间内冻结应用程序,在此期间,LeakCanary会显示以下的Toast:
3、Analyzing the heap分析堆转储文件
LeakCanary使用Shark解析.hprof文件,并在该堆转储中找到可能泄漏的对象。
这里有必要说一下:LeakCanary1.0和2.0进行解析.hprof文件的工具是不同的。
对于每个可能泄漏的对象。LeakCanary会分析这个对应为什么不能被垃圾回收期回收的引用路径。我们将在下一部分中学习分析泄漏跟踪:修复内存泄漏。
hprof文件分析完成后,LeakCanary将显示带有摘要的通知,并在Logcat中打印结果。
我们可以看下面的图片:
LeakCanary已经分析出两处不同类型的泄漏,共泄漏4个对象。
LeakCanary为每个泄漏跟踪创建一个signature,并将具有相同signature的泄漏(即,由同一bug引起的泄漏)组合在一起。
我们可以可以点击通知栏来查看更详细的泄漏信息,当然也也可以点击应用的那个Leaks的App图标来查看。LeakCanary会为每个安装的应用添加启动器图标。
每行对应一组具有相同signature的泄漏。 LeakCanary在应用程序首次使用该签名触发泄漏时将行标记为“新建”。
源码解析
前面说2.0不再需要我们手动执行初始化了, 所以我们看看2.0自动注册的实现.
答案就在AppWatcherInstaller这个类上,它继承了ContentProvider。
ContentProvider的特点就是不用显示调用初始化,在执行完application的初始化后就会调用contentProvider的onCreate()方法。正是利用这一点,leakcanary把注册写在了这里面,有系统自动调用完成,对开发者完全无感知。
初始化
在AppWatcherInstaller的onCreate中调用了AppWatcher的manualInstall方法
1 | internal sealed class AppWatcherInstaller : ContentProvider() { |
AppWatcherInstaller 有两个实现类,一个是 MainProcess,当我们使用 leakcanary-android 模块时,会默认使用这个,表示在当前 App 进程中使用 LeakCanary。另外一个类为 LeakCanaryProcess,当使用 leakcanary-android-process 模块代替 leakcanary-android 模块时,则会使用这个类,我们可以看下 leakcanary-android-process 的 Manifest 文件:
1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
AppWatcher#manualInstall()方法
1 | fun manualInstall(application: Application) { |
1 | // 代码位于:leakcanary/leakcanary-object-watcher-android/src/main/java/leakcanary/internal/InternalAppWatcher.kt |
InternalLeakCanary#invoke()
1 | /** |
内存泄漏的修复
文章参考:https://square.github.io/leakcanary/fundamentals-fixing-a-memory-leak/