Подтвердить что ты не робот

Много мусора в списке

У меня есть ListView, который использует пользовательский адаптер. Пользовательский адаптер getView использует все рекомендуемые методы:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    SuscriptionsViewsHolder holder;
    ItemInRootList item = mItemsInList.get(position);

    if (convertView == null) {
         convertView = mInflater.inflate(R.layout.label, null);

         holder = new SuscriptionsViewsHolder();
         holder.label = (TextView) convertView.findViewById(R.id.label_label);
         holder.icon = (ImageView) convertView.findViewById(R.id.label_icon);

        convertView.setTag(holder);
    } else {
        holder = (SuscriptionsViewsHolder) convertView.getTag();
    }

    String text = String.format("%1$s (%2$s)", item.title, item.unreadCount);
    holder.label.setText(text);
    holder.icon.setImageResource(item.isLabel ? R.drawable.folder : R.drawable.file );

    return convertView;
}

Однако, когда я прокручиваю, он вялый из-за большого сбора мусора:

GC_EXTERNAL_ALLOC freed 87K, 48% free 2873K/5447K, external 516K/519K, paused 30ms
GC_EXTERNAL_ALLOC freed 7K, 48% free 2866K/5447K, external 1056K/1208K, paused 29ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2866K/5447K, external 1416K/1568K, paused 28ms
GC_EXTERNAL_ALLOC freed 5K, 48% free 2865K/5447K, external 1600K/1748K, paused 27ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2865K/5447K, external 1780K/1932K, paused 30ms
GC_EXTERNAL_ALLOC freed 2K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms
GC_EXTERNAL_ALLOC freed 2K, 48% free 2870K/5447K, external 1780K/1932K, paused 25ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms
GC_EXTERNAL_ALLOC freed 3K, 48% free 2870K/5447K, external 1780K/1932K, paused 25ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2871K/5447K, external 1780K/1932K, paused 28ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2871K/5447K, external 1780K/1932K, paused 26ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 27ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 29ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 26ms
GC_EXTERNAL_ALLOC freed <1K, 48% free 2870K/5447K, external 1780K/1932K, paused 34ms

Что кажется неправильным?

EDIT @12: 47 GMT:

На самом деле это немного сложнее, чем это. Мой интерфейс приложения основан на 2 частях. Один из них - мозг экрана, создание представлений, обработка пользовательского ввода и т.д. Другим является Fragment, если устройство имеет андроид 3.0, в противном случае это Activity.

GC произошел на моем устройстве Nexus One 2.3.3, поэтому используйте Activity. У меня нет моего Xoom со мной, чтобы проверить поведение с помощью Fragment.

Я мог бы отправить источник, если потребуется, но позвольте мне попытаться объяснить это:

  • RootList - это мозг пользовательского интерфейса. Это содержит:
    • a List<> элементов, которые будут помещены в ListView.
    • метод, который создает этот список из SQLite db
    • пользовательский BaseAdapter, который содержит в основном только метод getView, вставленный выше
  • RootListActivity является ListActivity, который:
    • использует XML-макет
    • У макета, конечно, есть список с id android.id.list
    • обратные вызовы Activity перенаправляются в класс RootList с использованием экземпляра RootList, созданного при создании действия (конструктор, а не onCreate)
    • в onCreate, я вызываю RootList методы, которые создадут список элементов, и установите данные списка в новый экземпляр моего пользовательского класса, полученного из BaseAdapter

EDIT on may 17th @9:36 PM GMT:

Здесь приведен код действия и класс, который делает вещи. http://pastebin.com/EgHKRr4r

4b9b3361

Ответ 1

Я нашел проблему. Мой XML-макет для этой операции:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <include android:id="@+id/rootlist_header" layout="@layout/pre_honeycomb_action_bar" />

    <ListView android:id="@android:id/list"
        android:layout_below="@id/rootlist_header"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:textColor="#444444"
        android:divider="@drawable/list_divider"
        android:dividerHeight="1px"
        android:cacheColorHint="#00000000" />

</RelativeLayout>

Если я удаляю android:cacheColorHint="#00000000", тяжелый GC отсутствует, а прокрутка гладкая!:)

Я не знаю, почему этот параметр был установлен, потому что он мне не нужен. Возможно, я скопировал слишком много, вместо того, чтобы фактически создать свой XML-макет.

СПАСИБО за вашу поддержку, я очень ценю вашу помощь.

Ответ 2

У меня также была проблема с android:cacheColorHint="#00000000". Мне нужно было использовать его, хотя, поскольку у меня есть фиксированное фоновое изображение.

Я обнаружил, что установка следующих свойств отключает кеширование списка виджетов Android, и список прокручивается гладко (GC не вызывается так часто):

android:scrollingCache="false"
android:animationCache="false"

Также, если вы хотите избавиться от цвета выбора по умолчанию, используйте это:

android:listSelector="#00000000"

Ответ 3

Вы должны использовать DDMS и Tracking Allocation Tracker, чтобы точно определить, что генерирует так много объектов. Обратите внимание, однако, что String.format() хорошо известен для создания больших объемов мусора.

http://developer.android.com/resources/articles/track-mem.html

Ответ 4

Я действительно просмотрел ваш код и немного взломал его, чтобы он работал на моих устройствах. Я могу подтвердить, что ваш ListAdapter в порядке, и проблема в другом месте.

Это то, что вы делаете в методе createDefaultLabelsList().

mItemsInList.clear();
ItemInRootList item;

do {
    item = new ItemInRootList();
    item.isLabel = true;
    item.id = c.getString(c.getColumnIndex(Labels.KEY_ID));
    item.title = Labels.label(item.id);
    //TODO: fix this label.label = c.getString(c.getColumnIndex(Labels.KEY_LABEL));
    item.unreadCount = c.getString(c.getColumnIndex(Labels.KEY_UNREAD_COUNT));
    mItemsInList.add(item);
} while (c.moveToNext());

Кажется, что не использование локальной переменной в вашем цикле является причиной вашей проблемы с производительностью, верьте или нет. Замените это следующим образом:

mItemsInList.clear();

do {
    ItemInRootList item = new ItemInRootList();
    item.isLabel = true;
    item.id = c.getString(c.getColumnIndex(Labels.KEY_ID));
    item.title = Labels.label(item.id);
    //TODO: fix this label.label = c.getString(c.getColumnIndex(Labels.KEY_LABEL));
    item.unreadCount = c.getString(c.getColumnIndex(Labels.KEY_UNREAD_COUNT));
    mItemsInList.add(item);
} while (c.moveToNext());

и наслаждайтесь быстрым списком! Это разрешило это для меня на htc hero (2.1) и Xoom (3.1), используя ListActivity.

Как я бы это сделал в первую очередь, но я не совсем уверен, как это объясняет вялое поведение с вашей первоначальной реализацией.

Я нашел это, потому что для того, чтобы воспроизвести вашу проблему, мне пришлось заменить способ создания списка, просто создал список из 10k случайных меток и наблюдал, как он прокручивается очень быстро. Затем я заметил, что мы не использовали точно такой же цикл, вы добавляли одну и ту же ссылку снова и снова, когда добавляли 10k разных ссылок. Я переключился на вашу реализацию и воспроизвел ошибку.

Ответ 5

this

 String text = String.format("%1$s (%2$s)", item.title, item.unreadCount);

сделает GC вызванным много раз.

String.format() выделяет некоторую временную память в дополнение к строке, которую она наконец создает.

другой способ сделать это.

используя класс StringBuilder, например.

StringBuilder builder = new StringBuilder(128);
@Override
public View getView(int position, View convertView, ViewGroup parent) {
    SuscriptionsViewsHolder holder;
    ItemInRootList item = mItemsInList.get(position);

    if (convertView == null) {
         convertView = mInflater.inflate(R.layout.label, null);

         holder = new SuscriptionsViewsHolder();
         holder.label = (TextView) convertView.findViewById(R.id.label_label);
         holder.icon = (ImageView) convertView.findViewById(R.id.label_icon);

        convertView.setTag(holder);
    } else {
        holder = (SuscriptionsViewsHolder) convertView.getTag();
    }    

String text = String.format("%1$s (%2$s)", item.title, item.unreadCount);удаp >

    builder.setLength(0);
    builder.append(item.title).append(" (").append(item.unreadCount).append(")");
    holder.label.setText(builder.toString());
    holder.icon.setImageResource(item.isLabel ? R.drawable.folder : R.drawable.file );

    return convertView;
}

Ответ 6

попробуйте holder.icon.setImageBitmap(); вместо holder.icon.setImageResource();

Ответ 7

Строковый текст = String.format( "% 1 $s (% 2 $s)", item.title, item.unreadCount); Может быть, String.format() создает много промежуточных строк, которые загрязняют GC. Попробуйте StringBuilder и попробуйте кэшировать строку, которую он создает. Также как @tmho сказал попробовать holder.icon.setImageDrawable(); вместо holder.icon.setImageResource(); потому что setImageResource() каждый раз создает объект Drawable. И каждый Drawable составляет около 50-60 байт. Похоже, что растровое изображение heave не дублируется в этом случае, просто извлекаемый контейнер, но этого может быть достаточно, чтобы вызвать периодические вызовы GC.

Ответ 8

Это начинается с 4.4.4 обновления KitKat. Повторить код уже нормальный код на нем, и эта проблема появилась. ListView теперь полностью недоступен из-за очень медленного прокрутки. (Nexus 4)

И я не использую какие-либо форматиры на нем.

Когда прокручивается окно, появляется большое сообщение GC.

Ответ 9

У меня возник подобный вопрос, в моем списке будут отображаться изображения из Интернета и их сохранение на SD-карте. При запуске приложения позже будет использовано локальное изображение, а затем, когда я прокручу список, на логарифмическом экране появится много GC, и представление немного ошеломит.

Я заметил, что когда приложение было запущено впервые, с изображениями из Интернета, список выглядит нормально.

Кстати, я использовал android-imagedownloader из http://code.google.com/p/android-imagedownloader/ и добавил код локального кэша файлов.

Итак, вот мое решение, когда оно загружает изображение из локального файла, я добавлю объект растрового изображения в кеш памяти (статический HashMap), после этого, когда прокрутка списка вверх и вниз, он получит изображение из кэша памяти, и партии GC больше не повторялись.