2

У меня есть ListView и это adapter «s getView метод, я возвращаю RelativeLayout с MyButton внутри него.Android TextView протечек с setMovementMethod

MyButton имеет textView, и внутри него есть слова, которые можно щелкнуть (ClickableSpan).

Чтобы сделать эту работу, я начинаю с таял следующую строку: textView.setMovementMethod(LinkMovementMethod.getInstance());

Все работает отлично, но MAT показывает, что MyButton утечек из-за textView. Когда я прокомментирую вышеприведенную строку, ничего не течет.

Должен ли я установить movementMethod на null? Но даже если это так, я не могу знать момент уничтожения кнопки, чтобы установить это на null, поскольку он находится внутри многих других видов.

Что я делаю неправильно? Как предотвратить эту утечку?

enter image description here

обновление

решаемые утечку, установив текст в пустую строку внутри onDetachedFromWindow, но я все еще пытаюсь найти документацию, связанную с этим поведением. Почему я должен установить textview на ""?

+1

try View.onDetachedFromWindow() – pskink

+0

@pskink Спасибо, установка 'motionMethod' на' null' не работало, но установка текста в '' '' работала. (в onDetachedFromWindow). Если вы также знаете причину этой утечки, отправьте сообщение в качестве ответа, чтобы я мог отметить ее как принятый ответ. Мне все еще интересно, почему происходит эта утечка. Нет документации, связанной с этим поведением. – frankish

+0

Я использовал [LeakCanary] (https://github.com/square/leakcanary) для отслеживания утечки памяти и в конечном итоге нашел, что «TextView.setMovementMethod()» был виновником. К сожалению, для меня установка метода перемещения в нуль, а текст в «onDetachedFromWindow()» не решил проблему. LeakCanary показывает, что он имеет какое-то отношение к 'ViewTreeObserver', не имеющему очиститель preDraw. Учитывая, что 'TextView' реализует' OnPreDrawListener', мне интересно, что это делает что-то фанки под капотом? –

ответ

1

Ваша проблема, скорее всего, вызвана NoCopySpan. До KitKat TextView создавал копию пролета и помещал его в Bundle в onSaveInstanceState() с использованием SpannableString. SpannableString по какой-то причине не отказывает NoCopySpans, поэтому сохраненное состояние содержит ссылку на исходный TextView. Это было fixed для последующих выпусков.

Установка текста в "" устраняет проблему, потому что исходный текст, содержащий NoCopySpan, является GC'd правильно.

LeakCanary «s предложил работу вокруг этого ...

Hack: чтобы исправить это, вы можете переопределить TextView.onSaveInstanceState(), а затем использовать отражение для доступа к TextView.SavedState.mText и ясно NoCopySpan охватывает.

Исключительная запись LeakCanary для этой утечки может быть found here.

+0

Я тоже могу воспроизвести это на Android 5.1.1. – Alex

0

Я столкнулся с другой утечкой памяти с 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().

Вы можете прочитать полное расследование в своем блоге TextView, ClickableSpan and memory leak и сыграть с sample project.