7

我有一个ListViewand 在它的adapter方法中,我在里面getView返回一个RelativeLayoutwith 。MyButton

MyButton有一个textView,我里面有可点击的单词(ClickableSpan)。

为了完成这项工作,我从以下行开始: textView.setMovementMethod(LinkMovementMethod.getInstance());

一切正常,但 MAT 显示MyButton由于textView. 当我注释掉上面的行时,没有任何泄漏。

我要设置movementMethodnull?但即使是这样,我也不知道按钮的破坏时刻来设置它,null因为它在许多其他视图中。

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

在此处输入图像描述

更新

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

4

5 回答 5

6

我在. TextView_ ClickableSpan_ 第一次点击设备的超链接和旋转后,由于NPE,无法再次点击。LinkMovementMethodFragment

为了弄清楚发生了什么,我进行了调查,结果如下。

TextView在静态内部类的实例中保存mText包含字段的副本。它只在特定条件下发生。在我的例子中,它是一个可点击部分,它是在第一次点击跨度之后设置的。ClickableSpanonSaveInstanceState()SavedStateSelectionLinkMovementMethod

接下来,如果存在已保存状态,则从duringTextView对 field 执行恢复,包括所有 span 。mTextTextView.SavedState.textonRestoreInstanceState()

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

因此,解决方案非常简单,但没有记录 -ClickableSpanonStart().

您可以阅读我的博客TextView、ClickableSpan 和内存泄漏的完整调查并使用示例项目

于 2018-01-13T17:49:47.843 回答
3

即使ClickableSpan在高于KitKat. 如果您查看 的实现,ClickableSpan您会注意到它没有 extend NoCopySpan,因此它会onSaveInstanceState()像@DmitryKorobeinikov 和@ChrisHorner 的回答中描述的那样泄漏。所以解决方案是创建一个自定义类来扩展ClickableSpanNoCopySpan.

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

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

编辑 事实证明,启用辅助功能服务后,此修复程序会导致某些设备崩溃。

于 2018-11-08T06:27:22.530 回答
2

您的问题很可能是由NoCopySpan. onSaveInstanceState()在 KitKat 之前,TextView 会使用 SpannableString制作 span 的副本并将其放置在 Bundle 中。SpannableString 出于某种原因不会删除 NoCopySpans,因此保存的状态包含对原始 TextView 的引用。这已为后续版本修复

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

LeakCanary建议的解决方法是......

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

LeakCanary 对此泄漏的排除条目可以在这里找到

于 2015-06-10T00:35:57.473 回答
2

在花了几个小时尝试这些答案后,我想出了自己的答案,最终奏效了。

我不确定这有多准确,也不明白为什么会这样,但事实证明将 myTextView的 motionMethod 设置为 null inonDestroy()解决了这个问题。

如果有人知道为什么,请告诉我。我很困惑,因为它似乎没有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 之前可能存在问题。

于 2019-01-16T08:09:14.200 回答
1

尝试在onStart()method.Like中初始化 ClickableSpan

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

在某些 Android 版本上,Span 存在问题。有时它会导致内存泄漏。本文中的更多信息TextView、ClickableSpan 和内存泄漏

我希望它会有所帮助。

于 2018-10-06T21:31:33.577 回答