Я борюсь с утечкой памяти, связанной с ListView. Я создал следующую небольшую программу, которая демонстрирует такое поведение.Утечка памяти при использовании ListView в Android
Что мне нужно сделать, это создать 2 LinearLayouts. У первого есть кнопка и элемент управления GListView. Код для GListView приведен ниже, но он просто подклассы ListView и реализует интерфейс ListAdapter. Когда GListView создан, он устанавливает свой адаптер для себя.
Теперь, когда вы нажимаете кнопку, я переключаюсь на второй LinearLayout. Этот макет имеет только одну кнопку. Когда вы нажимаете эту кнопку, я создаю новый 1-й макет с новым GListView и устанавливаю его как активный вид.
Запустите программу и переключайтесь между двумя видами 20 раз. Затем поднимите DDMS и заставьте сбор мусора. Затем выгрузите память и используйте анализатор памяти, и вы найдете 21 объект GListView. То есть, 20 GListViews, которые больше не связаны ни с чем, НЕ были освобождены.
Если я дамп памяти и посмотреть на один из GListViews, которые должны были переработаны, и список ссылок Incomming с помощью анализатора памяти, я получаю следующее:
Class Name | Shallow Heap | Retained Heap
-------------------------------------------------------------------------------------------------------
com.gabysoft.memoryleak.GListView @ 0x43e72270 Unknown | 672 | 3,528
|- host android.view.View$ScrollabilityCache @ 0x43e72560 | 80 | 584
|- this$0 android.widget.AbsListView$RecycleBin @ 0x43e72830 | 40 | 160
|- mCallback android.graphics.drawable.StateListDrawable @ 0x43e728a8 | 64 | 1,464
|- this$0 android.widget.AdapterView$AdapterDataSetObserver @ 0x43e730b8| 16 | 16
-------------------------------------------------------------------------------------------------------
Теперь, если я закомментируйте функцию setAdapter (this) 'в конструкторе GListView и повторите выше, я обнаружил, что осталось только 1 GListView. То есть, в этом случае все неиспользуемые GListViews были надлежащим образом переработаны.
Кто-то предложил создать частный класс в моем GListView для обработки интерфейса ListAdapter, и я пробовал это, но это не помогло. Я также попытался создать полностью отдельный публичный класс для обработки ListAdapter, но, увы, это тоже не работает.
Несомненно, существует какой-то способ убрать эти объекты, когда они больше не используются нигде. (Разве это не то, что сбор мусора?)
Любая помощь будет оценена. Я действительно вытягиваю свои волосы на этом.
Спасибо.
/*
* Activity
*/
package com.gabysoft.memoryleak;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
public class MemoryLeak extends Activity implements android.view.View.OnClickListener
{
LinearLayout ll2;
boolean page2 = false;
private LinearLayout CreateLayout()
{
LinearLayout ll = new LinearLayout(this);
Button btn1 = new Button(this);
ListView lv = new GListView(this);
btn1.setText("Press");
btn1.setLayoutParams(new LinearLayout.LayoutParams(100, 40));
btn1.setOnClickListener(this);
ll.addView(btn1);
ll.addView(lv);
return(ll);
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
CreateLayout();
LinearLayout ll = CreateLayout();
ll2 = new LinearLayout(this);
Button btn2 = new Button(this);
btn2.setText("Back");
btn2.setLayoutParams(new LinearLayout.LayoutParams(100, 40));
btn2.setOnClickListener(this);
ll2.addView(btn2);
setContentView(ll);
}
@Override
public void onClick(View v)
{
if (page2)
{
LinearLayout ll = CreateLayout();
setContentView(ll);
page2 = false;
}
else
{
setContentView(ll2);
page2 = true;
}
}
}
/*
* GListView
*/
package com.gabysoft.memoryleak;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class GListView extends ListView implements ListAdapter
{
Context m_context;
DataSetObserver m_observer = null;
public GListView(Context context)
{
super(context);
m_context = context;
setAdapter(this);
setChoiceMode(CHOICE_MODE_SINGLE);
}
/*
* ListAdapter
*/
@Override
public boolean areAllItemsEnabled()
{
return true;
}
@Override
public boolean isEnabled(int position)
{
return true;
}
@Override
public int getCount()
{
return(0);
}
@Override
public Object getItem(int position)
{
return null;
}
@Override
public long getItemId(int position)
{
return(position);
}
@Override
public int getItemViewType(int position)
{
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
TextView tv = new TextView(m_context);
tv.setText("Item");
return(tv);
}
@Override
public int getViewTypeCount()
{
return 1;
}
@Override
public boolean hasStableIds()
{
return false;
}
@Override
public boolean isEmpty()
{
return false;
}
@Override
public void registerDataSetObserver(DataSetObserver observer)
{
m_observer = observer;
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer)
{
m_observer = null;
}
}
@John Gaby: Отправьте ссылку на ZIP-файл, содержащий полный проект с этим кодом, с которым я могу скачать и играть, и я посмотрю на него. – CommonsWare
Спасибо, что посмотрели. Я не мог видеть, как я могу опубликовать файл вместе с этим комментарием, поэтому я загрузил его на свой сайт. Его можно скачать по адресу http://gabysoft.com/download/memoryleak.zip. Я запускаю его на эмуляторе (хотя я считаю, что это происходит и на реальном устройстве) с помощью Android 2.2 –
@ Джона Габи: Да, вы не можете прикреплять файлы к SO-вопросам, поэтому то, что вы там делали, было прекрасным. Дайте мне несколько часов, так как у меня есть обновление книги, я хотел бы сначала выйти из комнаты. – CommonsWare