Android TextView与setMovementMethod泄漏

时间:2015-02-16 10:23:38

标签: android listview memory-leaks textview linkmovementmethod

我有ListView并且在adapter的{​​{1}}方法中,我在其中返回getView RelativeLayout

MyButton有一个MyButton我内部有可点击的字词(textView)。

为了使这项工作,我从以下行开始: ClickableSpan

一切都很完美,但MAT显示textView.setMovementMethod(LinkMovementMethod.getInstance());MyButton而泄漏。当我在上面注释掉这一行时,没有任何泄漏。

我应该将textView设为movementMethod吗?但即便如此,我也无法知道按钮的破坏时刻,将其设置为null,因为它位于许多其他视图中。

我做错了什么?如何防止这种泄漏?

enter image description here

更新

通过将文本设置为null内的空字符串解决了泄漏问题,但我仍在尝试查找与此行为相关的文档。我为什么要将onDetachedFromWindow设置为textview

5 个答案:

答案 0 :(得分:3)

我在TextView内制作超链接时遇到了ClickableSpanLinkMovementMethodFragment的另一个内存泄漏。在第一次单击超链接和设备旋转后,由于NPE,无法再次单击它。

为了弄清楚发生了什么,我做了一个调查,结果就是这样。

TextViewmText期间将包含ClickableSpan的字段onSaveInstanceState()的副本保存到静态内部类SavedState的实例中。它只在某些条件下发生。在我的情况下,可点击部分是Selection,在第一次点击跨度后由LinkMovementMethod设置。

接下来,如果存在已保存的状态,TextView会在mText期间TextView.SavedState.text执行onRestoreInstanceState()字段onRestoreInstanceState()的恢复,包括所有范围。

这是一个有趣的部分。调用onStart()时?它是在ClickableSpan之后调用的。我在onCreateView()中设置了onStart()的新对象,但在ClickableSpan之后,旧对象替换了新对象,导致了大问题。

因此,解决方案非常简单但没有记录 - 在onStart()期间执行{{1}}的设置。

您可以在我的博客TextView, ClickableSpan and memory leak上阅读完整的调查内容并使用sample project进行播放。

答案 1 :(得分:2)

您的问题很可能是由NoCopySpan引起的。在KitKat之前,TextView会复制span并使用SpannableString将其放在onSaveInstanceState()的Bundle中。由于某种原因,SpannableString不会丢弃NoCopySpans,因此保存的状态包含对原始TextView的引用。对于后续版本,这是fixed

将文本设置为“”可以解决问题,因为包含NoCopySpan的原始文本已正确GC。

LeakCanary建议解决这个问题是......

  

Hack:要解决此问题,您可以覆盖TextView.onSaveInstanceState(),然后使用反射访问TextView.SavedState.mText并清除NoCopySpan跨度。

LeakCanary对此泄漏的排除条目可以是found here

答案 2 :(得分:2)

即使在高于ClickableSpan的版本上使用KitKat仍可能导致泄漏。如果您研究ClickableSpan的实现,您会发现它没有扩展NoCopySpan,因此它在onSaveInstanceState()中泄漏,就像@DmitryKorobeinikov和@ChrisHorner答案中所述。因此,解决方案是创建一个扩展ClickableSpanNoCopySpan的自定义类。

class NoCopyClickableSpan(
    private val callback: () -> Unit
) : ClickableSpan(), NoCopySpan {

    override fun onClick(view: View) {
        callback()
    }
}

答案 3 :(得分:1)

尝试使用onStart()方法初始化ClickableSpan。就像

onStart(){
super.onStart()
someTextView.setText(buildSpan());
}

某些Android版本的Span出现问题。有时会导致内存泄漏。本文的更多信息TextView, ClickableSpan and memory leak

我希望它会有所帮助。

答案 4 :(得分:1)

花了几个小时尝试这些答案之后,我想出了自己的最终成功的方法。

我不确定这有多准确,也不知道为什么会这样,但是事实证明,在TextView中将我的onDestroy()的motionMethod设置为null可以解决问题。

如果有人知道为什么,请告诉我。我很困惑,因为似乎LinkMovementMethod.getInstance()并未引用TextView或活动。

这是代码

override fun onStart() {
    ...
    text_view.text = spanString
    text_view.movementMethod = LinkMovementMethod
} 

override fun onDestroy() {
    text_view.text = ""
    text_view.movementMethod = null
}

它在没有设置text_view.text = ""的情况下工作了,但是由于@Chris Horner回答说KitKat之前可能有问题,所以我将其保留了。