背景

今天发现有个App存在严重的内存泄漏问题,通过安装LeakCanary找到大致方向,通过严格管理Timer、Presenter和Handler,轻松把它消耗的内容降低了100M。

过程

LeakCanary

LeakCanary是square公司提供的一个很好用的内存泄漏查找工具,与MAT不同的是,它会在你运行App时检查你的内存回收,并找到内存泄漏点,直接提示给你,比用MAT去分析日志的形式更加简单直接,不过缺点是LeakCanary的信息不像MAT那么多,很难查到更细微的问题。

LeakCanary的安装过程很简单,首先配置Gradle

android 内存泄漏关键字 android内存泄漏工具_移动开发

然后在自定义的Application中初始化

android 内存泄漏关键字 android内存泄漏工具_App_02

记得在Manifest中使用自定义的Application

android 内存泄漏关键字 android内存泄漏工具_java_03

运行App,LeakCanary会在App运行过程中不定期收集信息,如果发现有内存泄漏的问题,就会在通知栏中给出提示,如XXXActivity大概因为XXX泄露了XXM内存,比如我们运行LeakCanary在Github上的demo,得到的提示就会像下图这样

android 内存泄漏关键字 android内存泄漏工具_java_04

可见,直接指出了问题类和问题原因,比起MAT还是简单多了的

Timer

在检查中,在Presenter里忘记关闭一个Timer的task,在Activity的onDestroy事件中调用Presenter的onDestroy

android 内存泄漏关键字 android内存泄漏工具_android 内存泄漏关键字_05

在Presenter的onDestroy中执行销毁操作

android 内存泄漏关键字 android内存泄漏工具_移动开发_06

我们看到,在Presenter销毁元素时,做了三件事:

1.关闭线程(要先执行interrupt,否则休眠线程无法立即停止)

2.销毁Presenter中关联的View对象(否则会导致对应的Activity无法释放)

3.退出Timer定时任务

Presenter

再看一遍Presenter执行销毁操作的代码

android 内存泄漏关键字 android内存泄漏工具_移动开发_06

 

在上图中,我们看到Presenter在销毁时,一定要把View销毁掉,否则会导致对应的Activity或Fragment无法释放,在检查中发现有些Presenter没有写销毁,考虑在以后统一实现一个Presenter的基类,在基类中实现销毁View的代码

Handler

在检查中,发现有些对话框弹出后无法回收内存,在检查中发现了这样的代码

LeakHandler

这是我们一般接触到的典型的Handler写法,但是注意看提示,提示中说明了这里有泄漏风险,从避免内存泄漏的角度,应该改成这样的代码

android 内存泄漏关键字 android内存泄漏工具_移动开发_08

 

在这段写法中,我们用static内部类去重载了一个Handler,static内部类实际上会生成一个弱引用对象,这就不会产生内存泄漏。

不过这样一来,在static内部类中,我们就无法调用Activity的函数了,这就需要在这个Handler初始化时把Activity传进来,直接传进来的Activity还是可能造成内存泄漏,我们还要把它放到一个弱引用对象里,通过get()函数取得Activity对象并调用其函数。

总结

在这次的内存泄漏查找过程中,主要解决了三个问题

1.及时销毁所有的Timer定时任务

2.及时销毁Presenter中的View对象

3.检查所有的Handler,改为static+弱引用的实现方式

解决这些问题后,再次运行LeakCanary,已经不再输出内存泄漏的内容了,App消耗的内存也减少了100多M。

其实这里解决的内存泄漏问题都是很浅显的部分,能在这些地方出错,说明这个App的内存泄漏已经相当严重了,在日常编码中,还是要多留一份心才是。