#android #view #gallery #out-of-memory #recycle
#Android #Вид #Галерея #нехватка памяти #переработать
Вопрос:
Виджет Gallery по умолчанию на Android не перерабатывает просмотры — каждый раз, когда вызывается view для новой позиции, виджет всегда вызывает getView
метод адаптера с convertView
значением null.
При прокрутке назад и вперед создается множество просмотров, которые компонент recycler, в котором они хранятся в галерее, похоже, не перерабатывает достаточно быстро, что приводит к ситуации с ООМ.
Вы можете легко протестировать это с помощью нескольких изображений большого размера в качестве элементов вашей галереи, но в конечном итоге это приведет только к TextView. Также поместите оператор log со счетчиком в getView
метод вашего адаптера, чтобы увидеть, сколько новых просмотров создано.
Существует ли сторонний виджет, который ведет себя как галерея, но который также реализует переработку вида?
Комментарии:
1. Если ни у кого нет другого решения, вы могли бы взять исходный код для
Gallery
, изменить его соответствующим образом и использовать. Я вижу в коде, на что вы ссылаетесь. Я понятия не имею, почемуmakeAndAddView()
написано так, как оно есть.2. Приветствия @CommonsWare — Я уже начал это делать, но это некрасиво и означает копирование и редактирование довольно большого количества классов, поскольку в нем используется несколько защищенных элементов и методов, поэтому мне просто интересно, есть ли у кого-нибудь идеи получше.
Ответ №1:
В конце концов, я принял решение, согласившись с предложением @CommonsWare изменить исходный код Gallery. Для этого также требуется скопировать следующие файлы:
AdapterView
AbsSpinner
но это довольно просто.
После этого я изменил код, чтобы выполнить следующее:
RecycleBin
(AbsSpinner
)
- Размещайте объекты в утилизаторе один за другим, а не в соответствии с положением
- Извлекайте объекты из нижней части утилизатора, независимо от запрошенной позиции
- Существующая реализация предполагала, что каждая отдельная позиция в адаптере приводила к уникальному представлению. Вышеуказанные изменения хороши только в том случае, если ваша галерея содержит только один тип элементов, в противном случае вам нужно будет добавить какой-то ключ, основанный на типе элемента и требуемом количестве этого типа
Gallery
- Использовал отражение (тьфу), чтобы изменить закрытую
mGroupFlags
переменнуюViewGroup
, чтобы разрешить повторный порядок дочерних элементов — я также установил логическое значение, указывающее, был ли выполнен успешный доступ к полю, который я тестирую перед использованием компонента.- Удалены все вызовы для
mRecycler.clear()
- Количество элементов, которые галерея должна отображать, изменяется по мере прокрутки, и существующая реализация очистит переработчик при (а) вызове setSelection (б) возникновении прокрутки движения
С этими изменениями мой счетчик в моем newView
методе в моем адаптере достиг … 7.
Вот код (Размещен в общественном достоянии 08.03.07 под http://en.wikipedia.org/wiki/WTFPL )
Комментарии:
1. не могли бы вы опубликовать ссылку на исходные файлы для Gallery и двух других, которые вы упомянули?
2. Слишком долго публиковать все классы в одном ответе, поэтому я поместил их здесь: pastebin.com/FWyYTt4D
3. Огромное спасибо за публикацию этого! Он отлично работает по сравнению со встроенным виджетом Gallery. Жаль, что нам приходится прибегать к таким крайним мерам только для того, чтобы получить работающий ресайклер. Если бы я понимал, какой это будет болью, я бы выбрал другой виджет вместо Gallery. Ваша измененная галерея работает и решила мою проблему, хотя еще раз спасибо!
4. я не могу использовать предоставленный вами код … существует несколько исключений приведения к классу, а также OnItemClickListener также создает проблемы … 03-05 12:16:00.545: E / AndroidRuntime (30246): java.lang. ClassCastException: android.widget. Галерея $LayoutParams 03-05 12:16:00.545: E / AndroidRuntime(30246): в .EcoGallery.setUpChild(EcoGallery.java:773) 03-05 12:16:00.545: E / AndroidRuntime(30246): на com.exiticlabs.arsenallwp.EcoGallery.makeAndAddView (EcoGallery.java:752) 03-05 12:16:00.545: E / AndroidRuntime(30246): в .EcoGallery.layout(EcoGallery.java:646) .EcoGallery.onLayout(EcoGallery.java: 362)
5. @Akos Поскольку вы спросили, я размещаю код в открытом доступе под en.wikipedia.org/wiki/WTFPL
Ответ №2:
На самом деле есть альтернатива, хотя я лично ее не тестировал:
Комментарии:
1. EcoGallery основана на (устаревшей) Android Gallery, и это в основном исходный код Android Gallery с некоторыми изменениями (но в нем присутствуют те же ошибки, что и в старой галерее), хотя он лучше перерабатывает view. Но проблема с «прыгающей» галереей все еще существует, если вы загружаете изображения в фоновом режиме.
2. не уверен, в чем ошибка «jumping», но мне все равно не нравится вид галереи, и вы правы, что этот проект основан на исходном коде галереи, но они утверждают, что пытаются исправить этот вид. я думаю, что, по крайней мере, механизм view-recycling там исправлен.
3. Да, эта версия галереи выполняет лучшую (хотя и не «такую хорошую, как listview») работу по переработке просмотров. Самая большая проблема (которая была у оригинальной галереи и никогда не была исправлена даже после того, как она устарела) заключается в том, что когда вы загружаете свои изображения в фоновом режиме (как и следовало бы), они запускают макет, и это заставляет галерею привязывать «ближайшее изображение» к центру. это приводит к постоянному скачку при прокрутке и загрузке элементов (и размещении в галерее). Есть несколько обходных путей, которые устраняют проблему, но это халтурно. После загрузки изображений галерея работает нормально 🙂
4. Я все еще не уверен, о чем вы говорите. Если загружаются изображения и размеры просмотров основаны на них, конечно, это приведет к возникновению странных вещей, поскольку размеры просмотров меняются. это произойдет и для ListViews. вы должны использовать фиксированный размер элементов, чтобы избежать такой вещи. В любом случае, что касается библиотеки, я попробовал ее около месяца назад и отказался от нее из-за обнаруженного сбоя (происходит только после многократной быстрой прокрутки). Вот почему я думаю, что если вам необходимо использовать галерею, используйте вместо нее эту.
5. «это произойдет и для ListViews. вы должны использовать фиксированный размер элементов, чтобы избежать такой вещи.» Хммм нет, этого не происходит при просмотре списка. Откуда у вас эта идея? Это происходит потому, что исходная галерея привязывает элемент к center onLayout (который вызывается, когда ImageView выполняет setXXXXBackground(). Это была ошибка в галерее навсегда и, возможно, одна из многих причин, по которой Google решил просто отказаться от галереи и начать с нуля с (хорошо, но не на 100% похожих альтернатив) ViewPager и HorizontalScrollableView).
Ответ №3:
Я использовал патч от http://code.google.com/p/android/issues/detail?id=3376#c19
Ответ №4:
Очень поздно на вечеринку, но я изменил EcoGallery, чтобы сделать еще несколько вещей (и избежать некоторых сбоев).
Я назвал его TimelineGallery, и это та же ерунда, что и Галерея, но он может выполнять плавную прокрутку и не делает странных вещей, когда изображения загружаются асинхронно.
Чтобы продемонстрировать это, в примере используются Picasso и PullToRefresh.
Исходный код, авторские права и тому подобное принадлежат Google, поэтому обвиняйте их в создании такого дерьмового виджета.
Заключительное замечание: я не рекомендую использовать галерею, она старая, глючная, взломанная и, вероятно, никогда не будет поддерживаться. Проблема не в исправлении его ошибок, проблема в том, что вся архитектура Gallery неверна, и поэтому ее исправление невозможно без внесения дополнительных взломов.
Google понял это и устарел. Используйте ViewPager или Horizontalscrollist и учитывайте ограничения каждого из них.
Если вы все еще хотите продолжить и использовать эту «галерею», не стесняйтесь, это работает, но это может привести к сбою вашего приложения и расстроить вас.
Ответ №5:
Другой более быстрый способ решения проблем с OutOfMemory — попробовать / перехватить код, в котором вы декодируете изображение, и, если выдается исключение OutOfMemory, вы пытаетесь декодировать его с меньшим разрешением снова..
что-то вроде этого:
private static Bitmap decodeFile(File f, int size, int suggestedScale) {
int scale = 1;
Bitmap bmp = null;
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
// Find the correct scale value. It should be the power of 2.
int width_tmp = o.outWidth, height_tmp = o.outHeight;
if(suggestedScale > 0)
scale = suggestedScale;
else {
if (width_tmp >= height_tmp) {
scale = Math.round((float)(width_tmp) / size);
} else {
scale = Math.round((float)(height_tmp) / size);
}
}
if(scale < 2)
return BitmapFactory.decodeFile(f.getPath());
Debug.i(TAG, "width: " width_tmp " height: " height_tmp " scale: " scale);
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {
} catch(OutOfMemoryError e) {
Debug.i(TAG, "we retry it cause of an OutOfMemoryException");
return decodeFile(f, size, scale 1);
} catch(Exception e){
Debug.w(TAG, e);
}
return bmp;
}
Конечно, теперь возможно, что вы будете видеть разные разрешения одного и того же изображения в разное время — но, по крайней мере, ваша галерея больше не будет зависать, и вы всегда показываете максимально возможное разрешение.