У меня есть RecyclerView
, представляющий несколько изображений с помощью Picasso
. После прокрутки некоторого времени вверх и вниз у приложения заканчивается память с такими сообщениями:
E/dalvikvm-heap﹕ Out of memory on a 3053072-byte allocation.
I/dalvikvm﹕ "Picasso-/wp-content/uploads/2013/12/DSC_0972Small.jpg" prio=5 tid=19 RUNNABLE
I/dalvikvm﹕ | group="main" sCount=0 dsCount=0 obj=0x42822a50 self=0x59898998
I/dalvikvm﹕ | sysTid=25347 nice=10 sched=0/0 cgrp=apps/bg_non_interactive handle=1500612752
I/dalvikvm﹕ | state=R schedstat=( 10373925093 843291977 45448 ) utm=880 stm=157 core=3
I/dalvikvm﹕ at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
I/dalvikvm﹕ at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:623)
I/dalvikvm﹕ at com.squareup.picasso.BitmapHunter.decodeStream(BitmapHunter.java:142)
I/dalvikvm﹕ at com.squareup.picasso.BitmapHunter.hunt(BitmapHunter.java:217)
I/dalvikvm﹕ at com.squareup.picasso.BitmapHunter.run(BitmapHunter.java:159)
I/dalvikvm﹕ at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)
I/dalvikvm﹕ at java.util.concurrent.FutureTask.run(FutureTask.java:234)
I/dalvikvm﹕ at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
I/dalvikvm﹕ at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
I/dalvikvm﹕ at java.lang.Thread.run(Thread.java:841)
I/dalvikvm﹕ at com.squareup.picasso.Utils$PicassoThread.run(Utils.java:411)
I/dalvikvm﹕ [ 08-10 18:48:35.519 25218:25347 D/skia ]
--- decoder->decode returned false
То, что я отмечаю при отладке:
- При установке приложения на телефоне или виртуальном устройстве изображения загружаются по сети, как и должно быть. Это видно по красному треугольнику в верхнем левом углу изображения.
- При прокрутке, чтобы изображения перезагружались, они извлекаются с диска. Это видно по синему треугольнику в верхнем левом углу изображения.
- При прокрутке некоторых изображений некоторые изображения загружаются из памяти, как видно из зеленого треугольника в верхнем левом углу.
- После прокрутки еще больше возникает исключение из памяти и загрузка прекращается. На изображениях, которые в настоящее время не хранятся в памяти, отображается только изображение-заглушка, а в памяти отображаются зеленым треугольником.
Здесь - образ образца. Он довольно большой, но я использую fit()
для уменьшения объема памяти в приложении.
Итак, мои вопросы:
- Не следует ли загружать изображения с диска, когда кеш памяти заполнен?
- Являются ли изображения слишком большими? Сколько памяти я могу ожидать, скажем, 0,5 МБ изображения, потреблять при декодировании?
- В моем коде ниже есть что-то неправильное/необычное?
Настройка статического экземпляра Picasso при создании Activity
:
private void setupPicasso()
{
Cache diskCache = new Cache(getDir("foo", Context.MODE_PRIVATE), 100000000);
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setCache(diskCache);
Picasso picasso = new Picasso.Builder(this)
.memoryCache(new LruCache(100000000)) // Maybe something fishy here?
.downloader(new OkHttpDownloader(okHttpClient))
.build();
picasso.setIndicatorsEnabled(true); // For debugging
Picasso.setSingletonInstance(picasso);
}
Использование статического экземпляра Picasso в RecyclerView.Adapter
:
@Override
public void onBindViewHolder(RecipeViewHolder recipeViewHolder, int position)
{
Picasso.with(mMiasMatActivity)
.load(mRecipes.getImage(position))
.placeholder(R.drawable.picasso_placeholder)
.fit()
.centerCrop()
.into(recipeViewHolder.recipeImage); // recipeImage is an ImageView
// More...
}
ImageView
в файле XML:
<ImageView
android:id="@+id/mm_recipe_item_recipe_image"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:paddingBottom="2dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:clickable="true"
/>
Update
Кажется, что прокрутка RecyclerView
непрерывно увеличивает распределение памяти на неопределенный срок. Я проверил тест RecyclerView
, чтобы сопоставить официальную документацию , используя одно изображение для 200 CardView
с ImageView
, но проблема сохраняется. Большая часть изображений загружается из памяти (зеленая), а прокрутка плавная, но примерно каждая десятая ImageView
загружает изображение с диска (синий). Когда изображение загружается с диска, выполняется распределение памяти, тем самым увеличивая выделение в куче и, следовательно, самой куче.
Я попытался удалить свою собственную настройку глобального экземпляра Picasso
и использовать по умолчанию вместо этого, но проблемы одинаковы.
Я проверил монитор Android-устройств, см. изображение ниже. Это для Галактики S3. Каждое из распределений, выполняемых при загрузке изображения с диска, можно увидеть справа в разделе "Счет подсчета для каждого размера". Размер немного отличается для каждого размещения изображения, что тоже странно. Нажатие кнопки "Причина GB" делает самое правильное выделение 4,7 МБ.
Поведение одинаково для виртуальных устройств. На рисунке ниже показано его для AVS Nexus 5. Также здесь наибольшее распределение (10,6 МБ) уходит при нажатии "Причина GB".
Кроме того, здесь представлены изображения местоположений выделения памяти и потоки от Android Device Monitor. Повторяющиеся выделения выполняются в потоках Picasso
, а один, удаленный с помощью Cause GB
, выполняется в основном потоке.