事情是这样的,我在项目里有个自定义的 Dialog 是继承 DialogFragment 实现的,接入 LeakCanary 后经常会提示我这个地方存在内存泄漏,定位的地方也有点奇怪,是一个布局控件上。
心想不应该啊,但既然报出来了,还是一探究竟。
内存泄漏
简单来说就是对象该销毁时没有被销毁回收,引用还被别的地方持有导致回收不掉,最后就变成了孤魂野鬼,达到一定程度就会导致 OOM 的问题了。
原因追踪
根据它报的位置我想难道是控件对象没被释放?于是我手动在 onDestoryView 里将控件 downLayout 赋值为 null,重试之后发现又报另外一个控件泄漏。显然问题不在这,这可能只是个表象。(得出这个推论花了我蛮长时间调试,经过多种猜测和尝试,大概定位在 DialogFragment 消失的时候,我复现问题的方式是,快速的打开销毁 DialogFragment)
回头在看看 LeakCanary 打出来的栈信息,指向的是 Message ???一开始我是不信的,打死都不信的,后来真香。表面上看 DialogFragment 的使用不会跟消息有什么关系啊,怎么会报 Message 的问题呢?看来需要我们看源码了。
DialogFragment 本质上是 Fragment 以 Dialog 的形式浮在 Activity 上展现,这个 Fragment 包含了一个 Dialog 对象,这个 Dialog 对象其实对我们是不可见的,因为我们不会直接操作它,重点就在这个 Dialog 上。
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
//省略部分......
//Dialog 构造里会创建这个类,本质上是个 Handler 那肯定跟消息有关
mListenersHandler = new ListenersHandler(this);
}
//这个就是这个内部类,可以看到它对 Dialog 的持有是个弱引用,这其实是安全的,不会造成内存泄漏
private static final class ListenersHandler extends Handler {
private final WeakReference<DialogInterface> mDialog;
public ListenersHandler(Dialog dialog) {
mDialog = new WeakReferenc