onBitmapLoaded dell'oggetto Target non richiamato al primo caricamento


126

Nella mia funzione:

public void getPointMarkerFromUrl(final String url, final OnBitmapDescriptorRetrievedListener listener) {
final int maxSize = context.getResources().getDimensionPixelSize(R.dimen.icon_max_size);
Target t = new Target() {
  @Override
  public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
    if (bitmap != null)
      listener.bitmapRetrieved(getBitmapDescriptorInCache(url, bitmap));
    else
      loadDefaultMarker(listener);
  }

  @Override
  public void onBitmapFailed(Drawable errorDrawable) {
    loadDefaultMarker(listener);
  }

  @Override
  public void onPrepareLoad(Drawable placeHolderDrawable) {
  }
};

Picasso.with(context)
    .load(url)
    .resize(maxSize, maxSize)
    .into(t);
}

OnBitmapLoaded () non viene mai chiamato la prima volta che carico immagini. Ho letto alcuni argomenti come https://github.com/square/picasso/issues/39 che raccomandano di usare il metodo fetch (Target t) (sembra essere un problema di riferimento debole ...), ma questa funzione non è disponibile nell'ultima versione di Picasso (2.3.2). Ho solo un metodo fetch (), ma non posso usare contemporaneamente in (mytarget)

Potresti spiegarmi come usare fetch () con un target personalizzato, per favore? Grazie.

Doc: http://square.github.io/picasso/javadoc/com/squareup/picasso/RequestCreator.html#fetch--


1
assicurati di utilizzare okhttp 2.0.0, riscontro lo stesso problema quando utilizzo Picasso 2.3.2 con Okhttp 1.6.0
hakim,

github.com/square/okhttp afaik, è obbligatorio se si utilizza Picasso 2.3.2 per includere la libreria okhttp (e okio). stai usando Eclipse o Android Studio?
Hakim,

Sto usando IntelliJ. Ho visto le mie dipendenze elementari, non ho visto okhttp ... Picasso sembra funzionare senza di essa
psv

@psv come hai implementato la soluzione di seguito con i marker?
Mustafa Güven,

Risposte:


247

Come notato dagli altri intervistati (@lukas e @mradzinski), Picasso mantiene solo un riferimento debole Targetall'oggetto. Sebbene sia possibile memorizzare un riferimento forte Targetin una delle tue classi, questo può comunque essere problematico se i Targetriferimenti sono Viewin qualche modo, poiché in realtà manterrai anche un riferimento forte a quello View(che è una delle cose che Picasso ti aiuta esplicitamente a evitare).

Se ti trovi in ​​questa situazione, ti consiglio di taggare Targetil View:

final ImageView imageView = ... // The view Picasso is loading an image into
final Target target = new Target{...};
imageView.setTag(target);

Questo approccio ha il vantaggio di lasciare che Picasso gestisca tutto per te. Gestirà gli WeakReferenceoggetti per ciascuna delle tue viste - non appena non ne Targetavrai più bisogno, qualunque sia l' elaborazione dell'immagine verrà anche rilasciata, quindi non sarai bloccato con perdite di memoria dovute a target di lunga durata, ma il tuo Target durerà fintanto che la sua vista è viva.


15
Mi ha salvato la giornata. Grazie.
cy198706,

24
Non ho una vista immagine, come posso risolvere questo problema? Quando hai a che fare con questo tipo di situazioni, la gc è il tuo peggior nemico
tim687

3
Puoi persino memorizzarlo in un ArrayList <Target> e funzionerà, ricordati di cancellare quell'arraylist :-)
Oliver Dixon,

2
In onBitmapLoaded e onBitmapFailed, sto anche eseguendo imageView.setTag (null) dopo aver elaborato la bitmap. Non è necessario?
Jaguar,

1
Grazie! Mi ha appena salvato la vita :)
yusufiga,

55

Picasso non contiene un forte riferimento all'oggetto Target, pertanto viene raccolto in modo non corretto e onBitmapLoadednon viene chiamato.

La soluzione è abbastanza semplice, basta fare un forte riferimento a Target.

public class MyClass {
   private Target mTarget = new Target() {...};

   public void getPointMarkerFromUrl(final String url, final OnBitmapDescriptorRetrievedListener listener) {

         Picasso.with(context)
         .load(url)
         .resize(maxSize, maxSize)
         .into(mTarget);
   }
}      

2
O crea il tuo Viewattrezzo Target.
dnkoutso,

nei documenti, dice che devi eseguire l'override Object.equals(Object)e i Object.hashCode()metodi. hai un campione funzionante?
chip

dove è scritto? Ho ancora il mio problema anche facendo un forte riferimento al mio Target ().
psv,

Ora ho installato okHttp, è un po 'più veloce da caricare ma ho ancora lo stesso problema al primo avvio. Qualche idea ?
psv,

@psv: hai risolto il problema del primo lancio di Picasso? Ho lo stesso problema? Se hai risolto come l'hai risolto?
TheDevMan

25

Se avessi ImageView lo farei semplicemente in questo modo: imageView.setTag (target);

Uso la prossima soluzione per caricare Bitmap nelle notifiche, quindi ho bisogno solo di bitmap.

Quindi crea Set strega memorizzerà gli oggetti Target e li rimuoverà al termine del caricamento.

final Set<Target> protectedFromGarbageCollectorTargets = new HashSet<>();

private void loadBitmap(String url) {
   Target bitmapTarget = new BitmapTarget(nEvent);
   protectedFromGarbageCollectorTargets.add(bitmapTarget);
   Picasso.with(context).load(url).into(bitmapTarget);
}

class BitmapTarget implements Target {

        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom loadedFrom) {

                    //handle bitmap
                    protectedFromGarbageCollectorTargets.remove(this);
                }
            }
        }

        @Override
        public void onBitmapFailed(Drawable drawable) {
            protectedFromGarbageCollectorTargets.remove(this);
        }

        @Override
        public void onPrepareLoad(Drawable drawable) {

        }
    }

13
ImageView profile = new ImageView(context);
        Picasso.with(context).load(URL).into(profile, new Callback() {
            @Override
            public void onSuccess() {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {//You will get your bitmap here

                        Bitmap innerBitmap = ((BitmapDrawable) profile.getDrawable()).getBitmap();
                    }
                }, 100);
            }

            @Override
            public void onError() {

            }
        });

1
Ha risolto anche il mio problema. volevo usarlo con notifica. a volte l'immagine veniva scaricata con Target ea volte no. ma dopo aver usato ImageView sono stato in grado di caricare immagini ogni volta
Raveesh GS

1
nel mio caso, tranne tutto, questa è stata la soluzione migliore!
Noor Hossain,

4

Ecco la soluzione per coloro che non utilizzano una vista. Questo metodo di supporto utilizza un elenco per memorizzare temporaneamente l'oggetto di destinazione fino a quando non viene restituito un risultato in modo che non venga visualizzato:

private List<Target> targets = new ArrayList<>();

public void downloadBitmap(final Context context, final String url, final MyCallback callback) {
    Target target = new Target() {

        @Override
        public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
            targets.clear();
            callback.onSuccess(bitmap);
        }

        @Override
        public void onBitmapFailed(Exception e, Drawable errorDrawable) {
            targets.clear();
            callback.onFailure(null);
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
        }
    };
    targets.add(target);
    Picasso.with(context).load(url).into(target);
}

3

Come diceva (e citando) @lukas, Picasso non ha un forte riferimento all'oggetto Target. Per evitare la garbage collection è necessario avere un forte riferimento all'oggetto.

Informazioni sul metodo fetch (). È abbastanza chiaro nella documentazione che fetch () non deve essere usato con ImageView né Target, è solo per "riscaldare" la cache e nient'altro, quindi non sarai in grado di usarlo come volere.

Ti consiglio di tenere un forte riferimento come spiegato @lukas, dovrebbe funzionare. In caso contrario, apri un nuovo problema nella pagina GitHub del progetto.


3

Ho riscontrato un problema simile e tenere il riferimento al target non mi ha aiutato affatto, quindi ho usato il seguente codice che restituisce un Bitmap:


Bitmap bitmap = picasso.with(appContext).load(url).get();

sul lato negativo -> non c'è callback e non è possibile chiamare questa funzione sul thread principale, è necessario eseguire questa funzione su un thread in background come nell'esempio seguente:


handlerThread = new HandlerThread(HANDLER_THREAD_NAME);
handlerThread.start();

Handler handler = new Handler(handlerThread.getLooper());
handler.post(new Runnable() {
    @Override
    public void run() {
        Bitmap bitmap = null;
        try {
            bitmap = picasso.with(appContext).load(url).get();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (bitmap != null) {
                //do whatever you wanna do with the picture.
                //for me it was using my own cache
                imageCaching.cacheImage(imageId, bitmap);
            }
        }
    }
});

Un'altra cosa che funziona molto meglio è solo usando Glide!

Avevo bisogno di usarli entrambi poiché lo scopo del mio progetto era di usare 2 API di download di immagini diverse per mostrare una galleria di immagini e dare all'utente la possibilità di scegliere quale API usare.

Devo dire che sono rimasto stupito dai risultati, l'apide di Glide ha funzionato perfettamente in ogni aspetto (l'obiettivo di Glide non ha un riferimento debole) mentre Picasso mi ha dato l'inferno (quella era la prima volta che usavo Glide, di solito ho usato Picasso finora, sembra che oggi cambierà ^^).


0

Avevo affrontato lo stesso problema ma quando cambio la dipendenza come indicato di seguito, ora funziona correttamente.

 implementation 'com.squareup.picasso:picasso:2.5.2'
 implementation 'com.squareup.okhttp:okhttp:2.3.0'
 implementation 'com.squareup.okhttp:okhttp-urlconnection:2.3.0'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.