如何使用LeakCanary处理安卓中的内存泄漏问题

465 阅读5分钟

使用LeakCanary处理Android中的内存泄露问题

本教程将帮助你了解如何使用LeakCanary 来检测Android应用程序中的内存泄漏。LeakCanary不仅可以检测内存泄漏,还可以帮助你减少内存泄漏。

由于OutOfMemory (OOM)错误导致的应用程序崩溃,内存泄漏的发生可能导致用户体验不佳。

前提条件

要完成这个教程,读者应该有。

  • 安装了[Android Studio]。
  • 对[Kotlin]编程语言有基本了解。

目标

在本教程结束时,读者将。

  • 理解内存泄漏及其原因。
  • 理解什么是LeakCanary
  • 知道避免内存泄露的最佳编程实践。

内存泄漏

每当一个对象不再使用时,垃圾收集器总是负责将其作为堆中的垃圾移除。

如果垃圾收集器无法执行其任务,就会出现一种被称为内存泄漏的情况。

当一个应用程序无法从一个对象中释放内存时,也会发生内存泄漏,该对象不再使用。

应用程序崩溃会导致糟糕的用户体验,应该加以避免。因此,处理内存泄漏很重要。

是什么导致了Android应用程序中的内存泄漏

以下是可能导致Android应用程序内存泄漏的常见做法。

  1. Fragment.onDestroyView() 方法中没有清除Fragment的视图字段就把Fragment 实例添加到后栈。

如果一个片段的实例被添加到后堆栈中,在onDestroyView 方法调用过程中,应该清除它以移除其引用。这将有助于避免内存泄漏。

  1. 将一个Activity 的实例作为Context 字段存储在一个对象中,该对象因配置变化而幸存于活动再创建。

在应用程序配置变化期间,各种任务在后台线程中运行。当一个活动的实例被存储在一个能在活动重现中存活的对象中时,很可能会发生内存泄漏。

  1. 忘了解除对指向生命循环对象的监听器、广播接收器或RxJava订阅的注册。
  2. 在后台线程中存储对一个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应用会自动安装。

Leaks App

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

Detecting Leak

2.倾倒堆

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

Dumping Leak

3.分析堆

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

Analyzing Leak

4.将泄漏的内容分类

这是最后一步,LeakCanary显示内存泄漏的位置,并将导致泄漏的对象用红色标注出来。

因此,开发人员可以参考代码并纠正它以避免内存泄漏。

Categorizing Leak

总结

LeakCanary是一个强大的泄漏检测库。

当你想把你的应用程序部署到生产中时,应该删除LeakCanary库,以避免把Leaks 应用程序给用户。

要删除LeakCanary,请进入build.gradle 文件,删除你添加的LeakCanary库,然后重建项目。