我有ListView
并且在adapter
的{{1}}方法中,我在其中返回getView
RelativeLayout
。
MyButton
有一个MyButton
我内部有可点击的字词(textView
)。
为了使这项工作,我从以下行开始:
ClickableSpan
一切都很完美,但MAT显示textView.setMovementMethod(LinkMovementMethod.getInstance());
因MyButton
而泄漏。当我在上面注释掉这一行时,没有任何泄漏。
我应该将textView
设为movementMethod
吗?但即便如此,我也无法知道按钮的破坏时刻,将其设置为null
,因为它位于许多其他视图中。
我做错了什么?如何防止这种泄漏?
更新
通过将文本设置为null
内的空字符串解决了泄漏问题,但我仍在尝试查找与此行为相关的文档。我为什么要将onDetachedFromWindow
设置为textview
?
答案 0 :(得分:3)
我在TextView
内制作超链接时遇到了ClickableSpan
,LinkMovementMethod
和Fragment
的另一个内存泄漏。在第一次单击超链接和设备旋转后,由于NPE,无法再次单击它。
为了弄清楚发生了什么,我做了一个调查,结果就是这样。
TextView
在mText
期间将包含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答案中所述。因此,解决方案是创建一个扩展ClickableSpan
和NoCopySpan
的自定义类。
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之前可能有问题,所以我将其保留了。