[英]Android TextView leaks with setMovementMethod
I have a ListView
and in it's adapter
's getView
method, I return a RelativeLayout
with MyButton
inside it.我有一个ListView
并且在它的adapter
的getView
方法中,我返回一个带有MyButton
的RelativeLayout
。
MyButton
has a textView
and I have clickable words inside it ( ClickableSpan
). MyButton
有一个textView
,里面有可点击的词( ClickableSpan
)。
To make this work, I start with thew following line: textView.setMovementMethod(LinkMovementMethod.getInstance());
为了完成这项工作,我从以下行开始: textView.setMovementMethod(LinkMovementMethod.getInstance());
Everything works perfectly but MAT shows that MyButton
leaks because of textView
.一切完美,但MAT显示, MyButton
泄漏,因为textView
。 When I comment out the line above, nothing leaks.当我注释掉上面的行时,没有任何泄漏。
Shall I set movementMethod
to null
?我应当设置movementMethod
到null
? But even if so, I can't know the destruction moment of the button to set that to null
as it is inside of many other views.但即使是这样,我也不知道按钮的销毁时刻将其设置为null
因为它在许多其他视图中。
What am I doing wrong?我究竟做错了什么? How to prevent this leak?如何防止这种泄漏?
update更新
Solved the leak by setting text to empty string inside onDetachedFromWindow
, but I am still trying to find a documentation related to this behaviour.通过在onDetachedFromWindow
文本设置为空字符串解决了泄漏onDetachedFromWindow
,但我仍在尝试查找与此行为相关的文档。 Why should I set the textview
to ""
?为什么我应该将textview
设置为""
?
I faced another memory leak with TextView
, ClickableSpan
, and LinkMovementMethod
while making hyperlinks inside a Fragment
.在Fragment
创建超链接时,我遇到了TextView
、 ClickableSpan
和LinkMovementMethod
另一个内存泄漏。 After the first click on the hyperlink and rotation of the device, it was impossible to click it again due to NPE.第一次点击设备的超链接和旋转后,由于NPE无法再次点击。
In order to figure out what's going on, I made an investigation and here is the result.为了弄清楚发生了什么,我进行了调查,这是结果。
TextView
saves a copy of the field mText
, that contains ClickableSpan
, during the onSaveInstanceState()
into the instance of static inner class SavedState
. TextView
在onSaveInstanceState()
期间将包含ClickableSpan
的字段mText
的副本保存到静态内部类SavedState
的实例中。 It happens only under certain conditions.它仅在特定条件下发生。 In my case, it was a Selection
for the clickable part, which is set by LinkMovementMethod
after the first click on the span.在我的例子中,它是可点击部分的一个Selection
,它是在第一次点击跨度后由LinkMovementMethod
设置的。
Next, if there is a saved state, TextView
performs restoration for the field mText
, including all spans, from TextView.SavedState.text
during onRestoreInstanceState()
.接着,如果存在已保存的状态中, TextView
执行恢复为字段mText
,包括所有的跨度,从TextView.SavedState.text
期间onRestoreInstanceState()
Here is a funny part.这是一个有趣的部分。 When onRestoreInstanceState()
is called?何时onRestoreInstanceState()
? It's called after onStart()
.它在onStart()
之后onStart()
。 I set a new object of ClickableSpan
in onCreateView()
but after onStart()
the old object replaces new one which leads to the big problems.我在onCreateView()
设置了一个ClickableSpan
的新对象,但是在onStart()
,旧对象替换了新对象,这导致了大问题。
So, the solution is quite simple but is not documented – perform setup of ClickableSpan
during onStart()
.因此,解决方案非常简单,但没有记录 - 在onStart()
期间执行ClickableSpan
设置。
You can read the full investigation on my blog TextView, ClickableSpan and memory leak and play with the sample project .您可以在我的博客TextView、ClickableSpan 和内存泄漏上阅读完整调查 ,并使用示例项目。
Using ClickableSpan
may still cause leaks even on versions higher than KitKat
.即使在高于KitKat
版本上,使用ClickableSpan
仍可能导致泄漏。 If you look into implementation of the ClickableSpan
you will notice that it doesn't extend NoCopySpan
, so it leaks in onSaveInstanceState()
like described in @DmitryKorobeinikov and @ChrisHorner answers.如果您查看ClickableSpan
实现,您会注意到它没有扩展NoCopySpan
,因此它在onSaveInstanceState()
泄漏,如@DmitryKorobeinikov 和@ChrisHorner 的答案中所述。 So the solution would be to create a custom class that extends ClickableSpan
and NoCopySpan
.因此,解决方案是创建一个扩展ClickableSpan
和NoCopySpan
的自定义类。
class NoCopyClickableSpan(
private val callback: () -> Unit
) : ClickableSpan(), NoCopySpan {
override fun onClick(view: View) {
callback()
}
}
EDIT It turned out that this fix leads to crashes on some devices when Accessibility services are enabled.编辑事实证明,当启用辅助功能服务时,此修复会导致某些设备崩溃。
Your issue is most likely caused by NoCopySpan
.您的问题很可能是由NoCopySpan
引起的。 Prior to KitKat, TextView would make a copy of the span and place it in a Bundle in onSaveInstanceState()
using a SpannableString.在 KitKat 之前,TextView 会复制 span 并使用 SpannableString 将其放置在onSaveInstanceState()
中的 Bundle 中。 SpannableString does not drop NoCopySpans for some reason, so the saved state holds a reference to the original TextView.由于某种原因,SpannableString 不会删除 NoCopySpans,因此保存的状态包含对原始 TextView 的引用。 This was fixed for subsequent releases.此问题已在后续版本中修复。
Setting the text to "" fixes the issue because the original text containing the NoCopySpan is GC'd properly.将文本设置为 "" 可以解决此问题,因为包含 NoCopySpan 的原始文本已正确 GC。
LeakCanary 's suggested work around for this is... LeakCanary建议的解决方法是......
Hack: to fix this, you could override TextView.onSaveInstanceState(), and then use reflection to access TextView.SavedState.mText and clear the NoCopySpan spans. Hack:要解决此问题,您可以覆盖 TextView.onSaveInstanceState(),然后使用反射访问 TextView.SavedState.mText 并清除 NoCopySpan 跨度。
LeakCanary's exclusion entry for this leak can be found here .可以在此处找到LeakCanary 对此泄漏的排除条目。
Try to initialize the ClickableSpan in onStart()
method.Like尝试在onStart()
方法中初始化 ClickableSpan。喜欢
onStart(){
super.onStart()
someTextView.setText(buildSpan());
}
There is problem with Span on some Android versions.某些 Android 版本上的 Span 存在问题。 Sometimes it causes memory leaks.有时它会导致内存泄漏。 More info in this article TextView, ClickableSpan and memory leak本文中的更多信息TextView、ClickableSpan 和内存泄漏
I hope it will help.我希望它会有所帮助。
After spending a few hours trying these answers out I came up with my own that finally worked.在花了几个小时尝试这些答案后,我想出了自己的答案,最终奏效了。
I'm not sure how accurate this is and don't understand why this is but it turned out that setting my TextView
's movementMethod to null in onDestroy()
solved the problem.我不确定这是多么准确,也不明白为什么会这样,但结果证明在onDestroy()
中将我的TextView
的运动方法设置为 null 解决了这个问题。
If anyone knows why please tell me.如果有人知道为什么请告诉我。 I'm so boggled because it doesn't seem like LinkMovementMethod.getInstance()
has a reference to the TextView
or the activity.我很困惑,因为LinkMovementMethod.getInstance()
似乎没有对TextView
或活动的引用。
Here's the code这是代码
override fun onStart() {
...
text_view.text = spanString
text_view.movementMethod = LinkMovementMethod
}
override fun onDestroy() {
text_view.text = ""
text_view.movementMethod = null
}
It worked without setting text_view.text = ""
but I kept it their because of @Chris Horner answer that there might be a problem prior to KitKat.它在没有设置text_view.text = ""
情况下工作,但我保留了它,因为@Chris Horner 回答说在 KitKat 之前可能存在问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.