然而想法很美好,现实很残酷啊,在super.onActivityCreate()方法中默认已经调用了mDialog.setOnCancelListener(this)和mDialog.setOnDismissListener(this)。此时获取的Message有可能是消息池中的某一条消息,而这条消息刚好被一个消息循环所持有不能释放的话,那么这个内存泄漏的问题依然无法解决,所以说这只是一个治标不治本的方法。

  • 建议第三方库一直发送空的消息,保持第三方库的消息循环消息队列一直不为空。

LeakCanary提供了一种解决方案:建议第三方库一直发送空的消息,保持第三方库的消息循环消息队列一直不为空。这种方式只能是提前知道哪个第三方库创建了自己的消息循环,

才能向这个消息循环中发送空消息,这并不能覆盖到所有的第三方创建的消息循环。而且,不断的向一个阻塞线程中发消息,线程时刻处于运行状态,占用线程空间资源。因此,此方案对于客户端开发来说并不可行。

  • 重写DialogFragment

直接拷贝DialogFragment代码至LeakDialogFragment类中,放弃实现DialogInterface.OnCancelListener和DialogInterface.OnDismissListener两个接口,

将这两个接口用静态内部类加弱引用的方式实现,然后将这个静态内部类设置到对应的内部Dialog当中去。

这的确是一个治标又治本的方案,但是工程量略大,而且本来DialogFragment是有谷歌官方维护的,现在变成了由你维护,如果未来官方发现了DialogFragment中产生了bug,默默修复了,那么你复制出来的这个类如何更新同步更新呢?

我不随流


其实一路过来无论是网上的资料还是LeakCanary都是告诉我们是说是DialogFragment发生了内存泄漏,但是罪魁祸首真的是DialogFragment吗?罪魁祸首是DialogFragment内部的Dialog啊,我们为什么一直揪着DialogFragment不放呢?为什么一直想着给DialogFragment治病呢?能不能给DialogFragment它内部的Dialog治治啊?

通过查看DialogFragment的源码我们发现它内部的mDialog是通过onCreateDialog方法生成的,而且这个方法是开放的。那么我们能不能通过重写这个方法,返回一个不会对DialogFragment持有强引用的Dialog不就完事了吗?

那么我们就重写一个Dialog名为NoLeakDialog:

public class NoLeakDialog extends Dialog {

public NoLeakDialog(@NonNull Context context, int themeResId) {

super(context, themeResId);

}

@Override

public void setOnCancelListener(@Nullable OnCancelListener listener) {

// 空实现,不持有外部的引用

}

@Override

public void setOnShowListener(@Nullable OnShowListener listener) {

// 空实现,不持有外部的引用

}

@Override

public void setOnDismissListener(@Nullable OnDismissListener listener) {

// 空实现,不持有外部的引用

}

@Override

public void setCancelMessage(@Nullable Message msg) {

// 空实现,不持有外部的引用

}

@Override

public void setDismissMessage(@Nullable Message msg) {

// 空实现,不持有外部的引用

}

}

然后在我们的继承的DialogFramment的onCreateDialog方法中返回我们的NoLeakDialog即可。

至此我自己内心不得不为我这个高级划水师惊人的隐藏bug能力叹服,赶紧泡一杯枸杞喝起来准备下一轮的划水。

几杯枸杞水下肚,正准备倒计时下班时,测试带着奸笑跑过来说你这个弹窗不对啊,我点击了空白处隐藏了弹窗,跳转到别的页面后再返回,这个弹窗又自己弹出来了。。。

此时我怀着高级划水师应有的自信直接怼回去说肯定是你的操作方式有问题,一边私底下偷偷打开AS调试起来。。。。

一顿操作猛如虎之后我们发现按返回键和点击空白区域返回键只是调用了Dialog的dismiss放,并没有调用DialogFragment的dismiss方法,因为点击空白区域或者返回键需要Dialog

回调DialogFragment才会调用DialogFragment的dismiss方法,但是我们在NoLeakDialog类中将这些监听器都变成了空实现,所以也就没有了回调。

而在DialogFragment的onDismiss方法方法中我们看到了官方的注释:

@Override

public void onDismiss(@NonNull DialogInterface dialog) {

if (!mViewDestroyed) {

// Note: we need to use allowStateLoss, because the dialog

// dispatches this asynchronously so we can receive the call

// after the activity is paused. Worst case, when the user comes

// back to the activity they see the dialog again.

dismissInternal(true, true);

}

}

注释已经很清楚地说明了DialogFragmen会再次弹出

对于这个问题,我们只要在我们NoLeakDialog中重写dismiss方法,将相关事件回调给DialogFragment,然后调用DialogFragment的dismiss或者dismissAllowingStateLoss方法即可。

所以我们最终NoLeakDialog的代码应该这样:

public class NoLeakDialog extends Dialog {

private WeakReference hostFragmentReference;

public void setHostFragmentReference(DialogFragment hostFragment) {

this.hostFragmentReference = new WeakReference<>(hostFragment);

}

public NoLeakDialog(@NonNull Context context, int themeResId) {

super(context, themeResId);

}

@Override

public void setOnCancelListener(@Nullable OnCancelListener listener) {

// 空实现,不持有外部的引用

}

@Override

public void setOnShowListener(@Nullable OnShowListener listener) {

// 空实现,不持有外部的引用

}

总结

**其实上面说了这么多,钱是永远赚不完的,在这个知识付费的时代,知识技能提升才是是根本!我作为一名8年的高级工程师,知识技能已经学习的差不多。**在看这篇文章的可能有刚刚入门,刚刚开始工作,或者大佬级人物。

像刚刚开始学Android开发小白想要快速提升自己,最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以这里分享一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

这么重要的事情说三遍啦!点赞+点赞+点赞!

【Android高级架构师系统学习资料】高级架构师进阶必备——设计思想解读开源框架

第一章、热修复设计
第二章、插件化框架设计
第三章、组件化框架设计
第四章、图片加载框架
第五章、网络访问框架设计
第六章、RXJava 响应式编程框架设计
第七章、IOC 架构设计
第八章、Android 架构组件 Jetpack


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
第二章、插件化框架设计
第三章、组件化框架设计
第四章、图片加载框架
第五章、网络访问框架设计
第六章、RXJava 响应式编程框架设计
第七章、IOC 架构设计
第八章、Android 架构组件 Jetpack

[外链图片转存中…(img-gszmww2d-1714800733534)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!

更多推荐