使用LeakCanary处理Android中的内存泄露问题
本教程将帮助你了解如何使用LeakCanary 来检测Android应用程序中的内存泄漏。LeakCanary不仅可以检测内存泄漏,还可以帮助你减少内存泄漏。
由于OutOfMemory (OOM)错误导致的应用程序崩溃,内存泄漏的发生可能导致用户体验不佳。
前提条件
要完成这个教程,读者应该有。
- 安装了[Android Studio]。
- 对[Kotlin]编程语言有基本了解。
目标
在本教程结束时,读者将。
- 理解内存泄漏及其原因。
- 理解什么是LeakCanary。
- 知道避免内存泄露的最佳编程实践。
内存泄漏
每当一个对象不再使用时,垃圾收集器总是负责将其作为堆中的垃圾移除。
如果垃圾收集器无法执行其任务,就会出现一种被称为内存泄漏的情况。
当一个应用程序无法从一个对象中释放内存时,也会发生内存泄漏,该对象不再使用。
应用程序崩溃会导致糟糕的用户体验,应该加以避免。因此,处理内存泄漏很重要。
是什么导致了Android应用程序中的内存泄漏
以下是可能导致Android应用程序内存泄漏的常见做法。
- 在
Fragment.onDestroyView()方法中没有清除Fragment的视图字段就把Fragment实例添加到后栈。
如果一个片段的实例被添加到后堆栈中,在onDestroyView 方法调用过程中,应该清除它以移除其引用。这将有助于避免内存泄漏。
- 将一个
Activity的实例作为Context字段存储在一个对象中,该对象因配置变化而幸存于活动再创建。
在应用程序配置变化期间,各种任务在后台线程中运行。当一个活动的实例被存储在一个能在活动重现中存活的对象中时,很可能会发生内存泄漏。
- 忘了解除对指向生命循环对象的监听器、广播接收器或RxJava订阅的注册。
- 在后台线程中存储对一个
Context的引用。
这将阻碍垃圾收集器回收由对Context 的引用所持有的对象,从而导致内存泄漏。
如何避免内存泄漏
- 避免在后台线程中保存上下文、活动或视图。
- 如果你不能控制内层类的生命周期,就使用一个静态的内层类,对外层类进行引用。避免在活动中使用非静态内类。
- 不要使用context-activity,尝试使用context-application。
什么是LeakCanary?
LeakCanary是一个开源的内存泄漏检测库,由Square组织开发。它可以检测并减少安卓应用程序中的内存泄漏。
它还会通知泄漏实际发生的位置。
使用LeakCanary很简单,因为它有预定义的功能。
它还简化了程序员的工作,因为它显示了内存泄漏的位置,这使得纠正和避免泄漏变得简单。
使用LeakCanary的原因
- 它有助于检测内存泄漏。
- 它可以帮助你修复内存泄漏,从而提高应用程序的性能。
检测内存泄漏的步骤
LeakCanary通过以下四个步骤处理内存泄漏。
- 检测被保留的对象。
- 倾倒堆。
- 分析堆。
- 将泄露的内容分类。
理论上讲够了,现在让我们看看LeakCanary的实际工作情况。
第1步:添加LeakCanary的依赖关系
在build.gradle (项目级)文件中,粘贴LeakCanary依赖关系并同步项目。
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
第2步:创建一个内存泄漏
在后台存储对上下文、视图或对象的引用会导致内存泄漏。
我们要做的是展示内存泄漏如何发生以及LeakCanary如何检测它们。
在你的MainActivity.kt ,添加以下代码,看看LeakCanary在检测内存泄漏时是如何工作的。
class MainActivity : AppCompatActivity() {
private var myBackground: Drawable? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
val textView = TextView(this)
textView.text = "Memory leaks are not good to applications"
if (myBackground == null) {
myBackground = getDrawable(R.drawable.ic_launcher_background)
}
textView.setBackgroundDrawable(myBackground)
setContentView(textView)
}
}
这段代码的问题不是传递给创建图像可画性的context 。
在这段代码中,问题在于private var myBackground: Drawable? = null (在MainActivity中声明的一个变量);它是以Activity 作为上下文创建的。
因此,有一个指向Activity的静态引用Drawable ,从而导致了泄漏。
为了避免这段代码中的内存泄漏,Drawable 应该使用应用程序上下文而不是TextView 来创建。
因此,你应该替换该语句。
myBackground = getDrawable(R.drawable.ic_launcher_background)
用。
myBackground = getApplicationContext().getResources().getDrawable(R.drawable.ic_launcher_background);
检测和报告内存泄漏的步骤
LeakCanary通过四个步骤处理内存泄漏。
1.检测已被保留的对象
当构建你的项目时,Leaks 应用程序也会与你的应用程序一起安装。由于LeakCanary库的存在,leaks应用会自动安装。

安装的泄漏应用程序显示通知,有最新的倾倒堆内存的计数。它还会显示泄漏检测的进度。

2.倾倒堆
在这一步,LeakCanary将堆内存转储到.hprof 文件(dump heap),该文件被保存到Android文件系统中。只有在达到阈值时才会转储堆内存。

3.分析堆
LeakCanary使用Shark(堆分析器)来解析.hprof 文件并定位堆转储中的保留对象。

4.将泄漏的内容分类
这是最后一步,LeakCanary显示内存泄漏的位置,并将导致泄漏的对象用红色标注出来。
因此,开发人员可以参考代码并纠正它以避免内存泄漏。

总结
LeakCanary是一个强大的泄漏检测库。
当你想把你的应用程序部署到生产中时,应该删除LeakCanary库,以避免把Leaks 应用程序给用户。
要删除LeakCanary,请进入build.gradle 文件,删除你添加的LeakCanary库,然后重建项目。