彻底解决系统DialogFragment泄露问题

问题背景:

     在使用DialogFragment时候,偶现出现内存泄露,如果咱们使用了leakCanary就会报如下错误:

androidx.fragment.ap
p.DialogFragment$2
instance
Leaking: UNKNOWN 
Retaining 956.8 kB in 1 5654 objects
Anonymous class implementing
android.content.DialogInterface$OnCan
celL istener
DialogFragment$2.this$0 

   当然也有的是

androidx.fragment.ap
p.DialogFragment$2
instance
Leaking: UNKNOWN 
Retaining 956.8 kB in 1 5654 objects
Anonymous class implementing
android.content.DialogInterface$OnDismissL istener
DialogFragment$2.this$0 

其实上面两个系统的泄露导致的原因完全一模一样,等下我们第二节介绍具体原因。我们先看下内存泄露帮我们怎么分析的,已leakCanary为例:

翻译出来其实就是消息队列的消息引用了我们DialogFragment对象,但是这里抛个疑问,理论上这种message导致的泄露,理论上消息执行完就没有泄露了,但是为啥leak canary还是报呢,误报么?其实不是,正常情况确实不用管,但是有种情况我们不得不管,这也是上面图片分析的原因:

 assuming these Message instances
will get GCed when the dialog is GCed. The
combination of these two things creates a
high potential for memory leaks as soon as
you use dialogs. These memory leaks might
be temporary, but some handler threads sleep
for a long time. To fix this, you could post
empty messages to the idle handler threads
from time to time. This won't be easy because
you cannot access all handler threads, but
a library that is widely used should consider
doing this for its own handler threads. This
leaks has been shown to happen in both
Dalvik and ART

翻译下来就是存在特殊情况,比如三方用了handleThread这个由于消息处理完被阻塞,然后我们又复用了这个消息,从而导致内存泄露,从对象引用链关系就是  handlerThread引用了messageA,然后messageA被DialogFragment复用了,由于handlerThread一直被阻塞从而导致一直释放不了,当然他也提供了一个解决方法就是通过不停发送空消息让咱们handleTread不阻塞,示例代码如下 :

handler.looper.queue.addIdleHandler {
    handler.obtainMessage().sendToTarget()
    true
}

这个方法明显有两个缺点:第一没办法集中处理,第二浪费资源,本来要休眠的线程你让他一直工作。

问题原因

这里我也不讲太多网上蛮多资料的,我主要解释上面遗留问题为啥这种情况下message会导致泄漏,因为message执行完就不应该有内存泄露。

我们先回到我们上文说的引用链关系 handlerThread 引用了MessageA,然后messageA被复用然后关联了DialogFragment引用,这就导致就是我们DialogFragment被回收了但是还是会泄露的原因,因为handlerThread 引用了MessageA。下面具体我们结合代码看:

handlerThread 引用了MessageA这个大家都比较明白。主要是MessageQueue。具体相信大家比我更熟悉Handler消息机制

 那为啥DialogFragment的onCancelListener和onDismissListener会出现泄漏呢?

 首先他们是内部类,这就引用了咱们的DialogFragment对象,那和message有啥关系,莫急我们呢继续看:

 关键就是上面红色框框的内容,因为他是复用来的,listener最终会被设置给message的obj对象。然后这个服用的message很可能被其他比如handlerThread持有。这就是泄漏关键原因了。

 解决方案

  其实就是打破强引用链关系,我们可以使用WeakReference弱引用包装一层,哈哈 又想到一句话在干不了咱们就包一层。核心代码如下:

 记得在这里调用:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值