为什么 AccessibilityManager.sInstance 会导致内存泄漏?
Is there a reason for why AccessibilityManager.sInstance would cause a memory leak?
我有一个包含片段的 activity。 运行 Leak Canary,我看到 activity 有内存泄漏。
我已经注释掉了从 activity 和片段到 activity 仅显示片段并且片段具有空 xml 布局的所有代码。我在任一文件或 xml.
中都没有可访问性
* AccessibilityManager.!(this[=11=])! (anonymous subclass of android.view.accessibility.IAccessibilityManagerClient$Stub)
* ↳ AccessibilityManager.!(mTouchExplorationStateChangeListeners)!
* ↳ CopyOnWriteArrayList.!(elements)!
* ↳ array Object[].!([2])!
* ↳ AccessibilityManagerCompat$TouchExplorationStateChangeListenerWrapper.!(mListener)!
* ↳ BaseTransientBottomBar$SnackbarBaseLayout.!(this[=11=])! (anonymous implementation of android.support.v4.view.accessibility.AccessibilityManagerCompat$TouchExplorationStateChangeListener)
* ↳ Snackbar$SnackbarLayout.mContext
* ↳ ContextThemeWrapper.mBase
* ↳ MessagesActivity
我遇到过类似的问题。我持有对 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);
}
好的,我真的想通了。这是 Snackbar 中的内存泄漏,这里是如何重现它的:https://github.com/GC-Xi/SnackbarBug
复制方式
- 创建一个 Snackbar 并在 Activity
中引用它
- 不要调用 Snackbar.show()
- 打开和关闭 Activity
- 请注意 Activity 未被垃圾回收,因为 snackbar 引用了它
原因
SnackbarBaseLayout
在构造函数中调用 addTouchExplorationStateChangeListener()
,在 onDetachedFromWindow()
中调用 removeTouchExplorationStateChangeListener()
。 addTouchExplorationStateChangeListener()
可能应该从 onAttachedToWindow()
调用,因为 SnackbarBaseLayout
没有附加到 window 除非调用 Snackbar.show()
。
解决方案 1
更新到 AndroidX 并改用 com.google.android.material.snackbar.Snackbar。
https://github.com/GC-Xi/SnackbarBug/tree/solution1
解决方案 2
除非您准备好显示 Snackbar,否则不要创建它。
https://github.com/GC-Xi/SnackbarBug/tree/solution2
我有一个包含片段的 activity。 运行 Leak Canary,我看到 activity 有内存泄漏。
我已经注释掉了从 activity 和片段到 activity 仅显示片段并且片段具有空 xml 布局的所有代码。我在任一文件或 xml.
中都没有可访问性* AccessibilityManager.!(this[=11=])! (anonymous subclass of android.view.accessibility.IAccessibilityManagerClient$Stub)
* ↳ AccessibilityManager.!(mTouchExplorationStateChangeListeners)!
* ↳ CopyOnWriteArrayList.!(elements)!
* ↳ array Object[].!([2])!
* ↳ AccessibilityManagerCompat$TouchExplorationStateChangeListenerWrapper.!(mListener)!
* ↳ BaseTransientBottomBar$SnackbarBaseLayout.!(this[=11=])! (anonymous implementation of android.support.v4.view.accessibility.AccessibilityManagerCompat$TouchExplorationStateChangeListener)
* ↳ Snackbar$SnackbarLayout.mContext
* ↳ ContextThemeWrapper.mBase
* ↳ MessagesActivity
我遇到过类似的问题。我持有对 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);
}
好的,我真的想通了。这是 Snackbar 中的内存泄漏,这里是如何重现它的:https://github.com/GC-Xi/SnackbarBug
复制方式
- 创建一个 Snackbar 并在 Activity 中引用它
- 不要调用 Snackbar.show()
- 打开和关闭 Activity
- 请注意 Activity 未被垃圾回收,因为 snackbar 引用了它
原因
SnackbarBaseLayout
在构造函数中调用 addTouchExplorationStateChangeListener()
,在 onDetachedFromWindow()
中调用 removeTouchExplorationStateChangeListener()
。 addTouchExplorationStateChangeListener()
可能应该从 onAttachedToWindow()
调用,因为 SnackbarBaseLayout
没有附加到 window 除非调用 Snackbar.show()
。
解决方案 1
更新到 AndroidX 并改用 com.google.android.material.snackbar.Snackbar。 https://github.com/GC-Xi/SnackbarBug/tree/solution1
解决方案 2
除非您准备好显示 Snackbar,否则不要创建它。 https://github.com/GC-Xi/SnackbarBug/tree/solution2