代码优化------Snackbar内存泄漏分析及解决

Snackbar内存泄漏分析及解决

1 分析工具

MemoryAnalyzer(MAT)

2 问题代码

 fun initView() {

	/*** 省略 ****/
    mSnackbar = Snackbar.make(view, "确定要退出吗?", Snackbar.LENGTH_LONG)
        mSnackbar!!.setAction("确定") { v ->
            finish()
        }

	/*** 省略 ****/
}

3 操作步骤

1.通过登录页进入到首页,首页是上面的代码创建一个SnackBar,然后通过一个按钮退出到登录页,反复重复登录三次,然后退出登录。手动执行gc
操作。然后下载后面得一段hprof文件分析内存泄漏

4 分析结果

发现出现MainActitiviy还有三个对象存在,因此我们来看看是谁引用了我们的MainActivity导致没有被释放掉。

结果截图

在这里插入图片描述

从图上我们可以看到里面有SnackBar的字样,然后我们去找找代码里面有snackbar代码的地方。

5 问题分析

在网上查了一下,这里问题一件分析得很清楚了,我就不再阐述了,

https://github.com/GC-Xi/SnackbarBug

6 解决方案

既然我们已经知道了是因为在初始化SnackbarBaseLayout中的时候有这样一句话

AccessibilityManagerCompat.addTouchExplorationStateChangeListener(this.accessibilityManager, this.touchExplorationStateChangeListener);

但是这个移除操作只有在 onDetachedFromWindow 方法中才会被调用到,因为上面的操作我们并没有调用Snackbar 的show方法, 也就不会被添加到window中,自然这个方法最后也不会被调用到。那么怎么做呢,我们既然从源码看到了在onDetachedFromWindow方法的代码:

 protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            if (this.onAttachStateChangeListener != null) {
                this.onAttachStateChangeListener.onViewDetachedFromWindow(this);
            }

            AccessibilityManagerCompat.removeTouchExplorationStateChangeListener(this.accessibilityManager, this.touchExplorationStateChangeListener);
        }

里面有一句removeTouchExplorationStateChangeListener的方法, 那么我们就自己来调用这个remove的方法把这个监听移除掉把。

7 解决代码

private fun fixSnackBarLeakageBug(snackbar: Snackbar) {
        try {
            val mSnackbarBaseLayoutField = snackbar.javaClass.superclass.getDeclaredField("view")
            if (mSnackbarBaseLayoutField != null) {
                mSnackbarBaseLayoutField.isAccessible = true
                val mSnackbarBaseLayoutView = mSnackbarBaseLayoutField.get(snackbar)
                val touchExplorationStateChangeListenerField = mSnackbarBaseLayoutView.javaClass.superclass.getDeclaredField("touchExplorationStateChangeListener")
                touchExplorationStateChangeListenerField.isAccessible = true
                val touchExplorationStateChangeListenerObj = touchExplorationStateChangeListenerField.get(mSnackbarBaseLayoutView) as AccessibilityManagerCompat.TouchExplorationStateChangeListener
                Log.i("====>", touchExplorationStateChangeListenerObj.toString())
                val accessibilityManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager;
                AccessibilityManagerCompat.removeTouchExplorationStateChangeListener(accessibilityManager, touchExplorationStateChangeListenerObj)
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }

通过分析源码,我们可以利用反射,拿到SnackbarBaseLayout对象里面的touchExplorationStateChangeListener,然后通过AccessibilityManager把这个移除掉。

最后我们在onDestory方法上加上调用这个方法的代码,再重新跑一遍刚才的流程,发现上图中的这个问题已经不存在了。 好了 可以继续分析内存泄漏问题了。

如果大家有用到snackBar大家也可以试试哦,有问题大家请指出,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值