繁体   English   中英

Android TextView 使用 setMovementMethod 泄漏

[英]Android TextView leaks with setMovementMethod

我有一个ListView并且在它的adaptergetView方法中,我返回一个带有MyButtonRelativeLayout

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

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

一切完美,但MAT显示, MyButton泄漏,因为textView 当我注释掉上面的行时,没有任何泄漏。

我应当设置movementMethodnull 但即使是这样,我也不知道按钮的销毁时刻将其设置为null因为它在许多其他视图中。

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

在此处输入图片说明

更新

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

Fragment创建超链接时,我遇到了TextViewClickableSpanLinkMovementMethod另一个内存泄漏。 第一次点击设备的超链接和旋转后,由于NPE无法再次点击。

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

TextViewonSaveInstanceState()期间将包含ClickableSpan的字段mText的副本保存到静态内部类SavedState的实例中。 它仅在特定条件下发生。 在我的例子中,它是可点击部分的一个Selection ,它是在第一次点击跨度后由LinkMovementMethod设置的。

接着,如果存在已保存的状态中, TextView执行恢复为字段mText ,包括所有的跨度,从TextView.SavedState.text期间onRestoreInstanceState()

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

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

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

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

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

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

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

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

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

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

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

可以在此处找到LeakCanary 对此泄漏的排除条目。

尝试在onStart()方法中初始化 ClickableSpan。喜欢

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

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

我希望它会有所帮助。

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

我不确定这是多么准确,也不明白为什么会这样,但结果证明在onDestroy()中将我的TextView的运动方法设置为 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 之前可能存在问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM