为什么AccessibilityManager.sInstance会导致内存泄漏?

时间:2019-04-15 22:12:41

标签: java android android-activity leakcanary

我有一个包含片段的活动。运行Leak Canary,我发现该活动存在内存泄漏。我已注释掉所有从活动和片段到活动仅显示片段且片段具有空xml布局的代码。我在文件或xml中都无法访问。

  • AccessibilityManager $ 1。!!(this $ 0)! (android.view.accessibility.IAccessibilityManagerClient $ Stub的匿名子类)
  • ↳AccessibilityManager。!(mTouchExplorationStateChangeListeners)!
  • ↳CopyOnWriteArrayList。!(元素)!
  • ↳数组Object []。!([2])!
  • ↳AccessibilityManagerCompat $ TouchExplorationStateChangeListenerWrapper。!(mListener)!
  • ↳BaseTransientBottomBar $ SnackbarBaseLayout $ 1。!(this $ 0)! (android.support.v4.view.accessibility.AccessibilityManagerCompat $ TouchExplorationStateChangeListener的匿名实现)
  • ↳Snackbar $ SnackbarLayout.mContext
  • ↳ContextThemeWrapper.mBase
  • ↳MessagesActivity

2 个答案:

答案 0 :(得分:2)

好的,我实际上已经知道了。这是Snackbar中的内存泄漏,下面是如何复制它的方法:https://github.com/GC-Xi/SnackbarBug

再现方式

  1. 创建一个Snackbar,并在Activity中引用它
  2. 不要调用Snackbar.show()
  3. 打开和关闭活动
  4. 请注意,该活动不是垃圾收集的,因为小吃店对此进行了引用

原因

SnackbarBaseLayout在构造函数中调用addTouchExplorationStateChangeListener(),在removeTouchExplorationStateChangeListener()中调用onDetachedFromWindow()。可能应该从addTouchExplorationStateChangeListener()调用onAttachedToWindow()的地方,因为除非调用SnackbarBaseLayout,否则Snackbar.show()不会附加到窗口。

解决方案1 ​​

更新到AndroidX,并改用com.google.android.material.snackbar.Snackbar。 https://github.com/GC-Xi/SnackbarBug/tree/solution1

解决方案2

除非准备好显示它,否则请不要创建它。 https://github.com/GC-Xi/SnackbarBug/tree/solution2

答案 1 :(得分:1)

我遇到了类似的问题。我当时持有对Snackbar的引用。我删除该引用后,此内存泄漏消失了。

更新

例如,替换

val snackbar = Snackbar.make(rootLayout, "Hello Snackbar", Snackbar.LENGTH_INDEFINITE)
snackbar.show()

使用

Snackbar.make(rootLayout, "Hello Snackbar", Snackbar.LENGTH_INDEFINITE).show()

我不知道为什么它可以解决我的问题。我无法在其他项目中重现此内存泄漏。基于堆栈跟踪,似乎BaseTransientBottomBar.onDetachedFromWindow()没有被系统调用,因此touchExplorationStateChangeListener并未从accessibilityManager中删除。同样,我不知道为什么会这样。以下是BaseTransientBottomBar.onDetachedFromWindow()的代码。

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

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