Ho un RecyclerView
che presenta diverse immagini utilizzando Picasso
. Dopo lo scorrimento po 'di tempo su e giù per l'applicazione esaurisce la memoria con messaggi come questo:Picasso: memoria esaurita
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
Le cose che nota durante il debug:
- Quando si installa l'applicazione su un telefono o di un dispositivo virtuale, le immagini vengono caricate sulla rete, che è come dovrebbe essere. Questo è visto dal triangolo rosso nell'angolo in alto a sinistra dell'immagine.
- Durante lo scorrimento in modo che le immagini vengano ricaricate, vengono recuperate dal disco. Questo è visto dal triangolo blu nell'angolo in alto a sinistra dell'immagine.
- Durante lo scorrimento, alcune delle immagini vengono caricate dalla memoria, come mostrato da un triangolo verde nell'angolo in alto a sinistra.
- Dopo averlo fatto scorrere ancora, si verifica l'eccezione di memoria esaurita e il caricamento si interrompe. Solo l'immagine segnaposto viene mostrata sulle immagini che non sono attualmente conservate in memoria, mentre quelle in memoria sono mostrate correttamente con un triangolo verde.
Here è un'immagine di esempio. È abbastanza grande, ma io uso fit()
per ridurre il footprint di memoria nell'app.
Quindi le mie domande sono:
- Non dovrebbe essere ricaricati le immagini dal disco quando la cache di memoria è piena?
- Le immagini sono troppo grandi? Quanta memoria posso aspettare un'immagine, diciamo 0.5 MB, da consumare quando decodificato?
- C'è qualcosa di sbagliato/insolito nel mio codice qui sotto?
Impostare l'istanza di Picasso statica durante la creazione del 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);
}
utilizzando l'istanza Picasso statica nel mio 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...
}
Il ImageView
in XML file:
<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"
/>
Aggiornamento
Sembra che scorrendo il RecyclerView
rende continuamente l'aumento di allocazione della memoria a tempo indeterminato. Ho eseguito il test RecyclerView
per abbinarlo allo official documentation, utilizzando una singola immagine per 200 CardView
s con un ImageView
, ma il problema persiste. La maggior parte delle immagini viene caricata dalla memoria (verde) e lo scorrimento è scorrevole, ma circa ogni decimo ImageView
carica l'immagine dal disco (blu). Quando l'immagine viene caricata dal disco, viene eseguita un'allocazione di memoria, aumentando così l'allocazione sull'heap e quindi sull'heap stesso.
Ho provato a rimuovere la mia configurazione dell'istanza globale Picasso
e invece l'impostazione predefinita, ma i problemi sono gli stessi.
Ho fatto un controllo con il dispositivo Android Monitor, vedere l'immagine qui sotto. Questo è per un Galaxy S3. Ciascuna allocazione eseguita quando un'immagine viene caricata dal disco può essere vista a destra sotto "Conteggio allocazione per dimensione". La dimensione è leggermente diversa per ogni allocazione dell'immagine, che è anche strana. Premendo "Causa GB" si disattiva l'allocazione più a destra di 4,7 MB.
il comportamento è lo stesso per i dispositivi virtuali. L'immagine sotto mostra per un Nexus 5 AVD. Anche qui, l'allocazione più grande (quella da 10,6 MB) scompare quando si preme "Cause GB".
Inoltre, qui immagini delle posizioni di allocazione di memoria e le filettature del monitor dispositivo Android. Le allocazioni ricorrenti vengono eseguite nei thread Picasso
mentre quello rimosso con Cause GB
viene eseguito sul thread principale.
Sto avendo lo stesso identico problema, anche con solo 25 elementi di riciclaggio, se si scorre su e giù continuamente si blocca con l'eccezione di memoria insufficiente. – Zapnologica