EditText вызывает утечку памяти
Введение:
У меня есть приложение, которое имеет следующую структуру:
ActionBar вверх (ActionBarSherlock)
ViewPagerIndicator ниже (для вкладок)
ViewPager (фрагменты хостов)
У меня есть проблема, что один из моих фрагментов вызывает довольно большую утечку памяти. Я сузил проблему до следующего случая:
Фрагмент, который вызывает утечку, ничего не делает, кроме раздувания макета в нем onCreateView
. Это делается следующим образом:
return inflater.inflate(R.layout.filter_auctions_fragment, container, false);
Ничего необычного здесь.
Файл макета включает в себя только ScrollView
, LinearLayout
и два EditText
в нем (включает в себя более обычные вещи, но я сузил проблему вплоть до этих представлений, чтобы сделать ее простой).
Теперь код, который использовался для добавления фрагмента: mTabsAdapter.addTab(tabName, ProblematicFragment.class);
mTabsAdapter
является экземпляром TabsAdapter
, класса, который расширяет FragmentPagerAdapter
библиотеки поддержки. Это довольно стандартный, поэтому я не включаю источник, чтобы этот вопрос был как можно короче.
Теперь смешная часть:
Это то, что происходит с кучей, когда я несколько раз поворачиваю свое устройство назад и вперед:
12-28 12:26:27.180: D/dalvikvm(18841): GC_CONCURRENT freed 530K, 7% free 10701K/11436K, paused 4ms+7ms, total 58ms
12-28 12:26:27.180: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 24ms
12-28 12:26:28.270: D/dalvikvm(18841): GC_CONCURRENT freed 737K, 8% free 11048K/11964K, paused 4ms+5ms, total 53ms
12-28 12:26:29.510: D/dalvikvm(18841): GC_CONCURRENT freed 789K, 8% free 11464K/12436K, paused 5ms+5ms, total 42ms
12-28 12:26:30.640: D/dalvikvm(18841): GC_CONCURRENT freed 888K, 9% free 11919K/12984K, paused 4ms+5ms, total 52ms
12-28 12:26:31.810: D/dalvikvm(18841): GC_CONCURRENT freed 903K, 8% free 12421K/13500K, paused 3ms+8ms, total 58ms
12-28 12:26:33.800: D/dalvikvm(18841): GC_CONCURRENT freed 1092K, 9% free 13005K/14272K, paused 4ms+6ms, total 59ms
12-28 12:26:33.800: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 20ms
12-28 12:26:36.000: D/dalvikvm(18841): GC_CONCURRENT freed 1355K, 11% free 13518K/15048K, paused 3ms+8ms, total 74ms
12-28 12:26:36.000: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 19ms
12-28 12:26:38.110: D/dalvikvm(18841): GC_CONCURRENT freed 1450K, 11% free 14106K/15720K, paused 3ms+11ms, total 72ms
12-28 12:26:40.450: D/dalvikvm(18841): GC_CONCURRENT freed 1530K, 11% free 14807K/16516K, paused 2ms+15ms, total 75ms
12-28 12:26:40.450: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 29ms
12-28 12:26:43.030: D/dalvikvm(18841): GC_CONCURRENT freed 1682K, 11% free 15591K/17452K, paused 3ms+10ms, total 66ms
12-28 12:26:43.030: D/dalvikvm(18841): WAIT_FOR_CONCURRENT_GC blocked 32ms
Ясно, что утечка памяти.
Да, я знаю, что это заставляет Activity быть воссозданным с нуля, и это то, что я хочу, потому что у меня разные макеты для пейзажных и портретных режимов. Тем не менее это не должно вызывать утечку памяти.
Я нашел источник этой проблемы. Это два EditText
, о которых я упоминал ранее. Как только я удалю их из макета и сделаю один и тот же тест (поверните назад и вперед). Это сообщения GC, которые я получаю:
12-28 12:21:41.270: D/dalvikvm(17934): GC_CONCURRENT freed 534K, 7% free 10853K/11576K, paused 3ms+7ms, total 44ms
12-28 12:21:42.560: D/dalvikvm(17934): GC_CONCURRENT freed 818K, 9% free 11113K/12108K, paused 11ms+9ms, total 95ms
12-28 12:21:44.680: D/dalvikvm(17934): GC_CONCURRENT freed 1036K, 10% free 11313K/12528K, paused 3ms+6ms, total 54ms
12-28 12:21:44.680: D/dalvikvm(17934): WAIT_FOR_CONCURRENT_GC blocked 15ms
12-28 12:21:47.420: D/dalvikvm(17934): GC_CONCURRENT freed 1089K, 10% free 11510K/12780K, paused 2ms+6ms, total 79ms
12-28 12:21:47.420: D/dalvikvm(17934): WAIT_FOR_CONCURRENT_GC blocked 39ms
12-28 12:21:50.200: D/dalvikvm(17934): GC_CONCURRENT freed 1317K, 12% free 11461K/12956K, paused 4ms+13ms, total 84ms
12-28 12:21:53.210: D/dalvikvm(17934): GC_CONCURRENT freed 1629K, 14% free 11148K/12956K, paused 3ms+7ms, total 47ms
12-28 12:21:55.580: D/dalvikvm(17934): GC_CONCURRENT freed 1056K, 13% free 11302K/12956K, paused 4ms+7ms, total 59ms
12-28 12:21:57.280: D/dalvikvm(17934): GC_CONCURRENT freed 1306K, 14% free 11200K/12956K, paused 5ms+5ms, total 82ms
12-28 12:21:59.420: D/dalvikvm(17934): GC_CONCURRENT freed 1035K, 12% free 11408K/12956K, paused 3ms+7ms, total 55ms
12-28 12:22:01.990: D/dalvikvm(17934): GC_CONCURRENT freed 1392K, 13% free 11352K/12956K, paused 4ms+9ms, total 54ms
12-28 12:22:01.990: D/dalvikvm(17934): WAIT_FOR_CONCURRENT_GC blocked 30ms
Теперь, что я хочу видеть!
ПОЧЕМУ!
Может кто-нибудь сказать мне, почему это происходит? Я хотел бы добавить, что я не поддерживаю ссылку на этот объект EditText
в любом месте своего приложения (обычно я это делаю, но даже когда я удалял все из них в целях тестирования, утечка все еще происходит).
Бонус - MAT Скриншоты утечки:
![Path to GC Roots (whtout soft/weak refs) for my Activity]()
![Path to GC Roots (whtout soft/weak refs) for my "ProblematicFragment"]()
Как вы можете видеть, есть 16 экземпляров фрагмента и активности, тогда как должно быть только одно.
EDIT:
Я заметил, что когда я вручную добавляю фрагмент в другую активность (используя FragmentManager.beginTransaction()
), утечки не происходит!!! Теперь я полностью смущен...
EDIT2:
Удаление атрибута android:id
EditText
исправляет его... Но теперь они довольно бесполезны...
Ответы
Ответ 1
Я нашел решение, которое соответствует моим потребностям.
Я проследил проблему до widget.EditableInputConnection
. Я думаю, что это поиск в системе предложений. Он также несет ответственность за сохранение моей активности, что вызывает утечку памяти.
Мне не нужны предложения, поэтому я хотел отключить его. Однако оказалось, что это сложно. EditText.setInputType
не работает, ни в xml, ни в коде.
В итоге я сделал следующее. Магия происходит в onCreateInputConnection()
:
public class MyEditText extends TextView {
public MyEditText(Context context) {
this(context, null);
}
public MyEditText(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.editTextStyle);
}
public MyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return null;
}
@Override
protected boolean getDefaultEditable() {
return true;
}
@Override
protected MovementMethod getDefaultMovementMethod() {
return ArrowKeyMovementMethod.getInstance();
}
@Override
public Editable getText() {
return (Editable) super.getText();
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, BufferType.EDITABLE);
}
/**
* Convenience for {@link Selection#setSelection(Spannable, int, int)}.
*/
public void setSelection(int start, int stop) {
Selection.setSelection(getText(), start, stop);
}
/**
* Convenience for {@link Selection#setSelection(Spannable, int)}.
*/
public void setSelection(int index) {
Selection.setSelection(getText(), index);
}
/**
* Convenience for {@link Selection#selectAll}.
*/
public void selectAll() {
Selection.selectAll(getText());
}
/**
* Convenience for {@link Selection#extendSelection}.
*/
public void extendSelection(int index) {
Selection.extendSelection(getText(), index);
}
@Override
public void setEllipsize(TextUtils.TruncateAt ellipsis) {
if (ellipsis == TextUtils.TruncateAt.MARQUEE) {
throw new IllegalArgumentException("EditText cannot use the ellipsize mode "
+ "TextUtils.TruncateAt.MARQUEE");
}
super.setEllipsize(ellipsis);
}
}
Где трюк - отказаться от InputConnection. Это устраняет предложения и устраняет утечку памяти.
Надеюсь, это поможет вам.
Ответ 2
Я столкнулся с той же проблемой с Samsung Galaxy S3.
Решение от @aslakjo не работало для меня. У меня уже отключены предложения
Я закончил замену android:id
на android:tag
для EditText
в макете XML. Теперь он отлично работает, мои фрагменты, которые содержались в viewpager, правильно собирают мусор, когда их больше не видно, не спрашивайте меня, почему.
Это устройство может зарегистрировать что-то на основе наличия id
.