Verwenden Sie Picasso, um einen Rückruf mit einer Bitmap zu erhalten

83

Ich verwende Picasso , um Bilder für meine App herunterzuladen.

Ich bin in einer Situation, in der ich auf die Bitmaperste zugreifen muss, bevor sie in die geladen wird ImageView. Die Anwesenheit der Downloader.ResponseKlasse scheint darauf hinzudeuten, dass dies möglich ist, aber ich kann keine Anwendungsbeispiele finden. Ich möchte nicht mehr Code schreiben, um diesen einen speziellen Fall asynchron zu behandeln, wenn dies mit Picasso möglich ist.

Kann mir jemand zeigen, wie es geht?

Steve M.
quelle

Antworten:

172

Ich habe die Antwort auf Github gefunden, falls sich jemand wundert:

private Target target = new Target() {
      @Override
      public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
      }

      @Override
      public void onBitmapFailed(Drawable errorDrawable) {
      }

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

private void someMethod() {
   Picasso.with(this).load("url").into(target);
}

@Override 
public void onDestroy() {  // could be in onPause or onStop
   Picasso.with(this).cancelRequest(target);
   super.onDestroy();
}

In diesem Beitrag wird empfohlen, keinen anonymen Rückruf zu verwenden und stattdessen eine Instanzvariable für das Ziel zu verwenden.

Steve M.
quelle
30
Idealerweise implementieren Sie Targeteine Ansicht oder ein Ansichtsinhaberobjekt direkt. Wenn Sie dies nicht tun, müssen Sie irgendwo einen starken Verweis auf die Instanz beibehalten, da sonst Müll gesammelt wird.
Jake Wharton
@JakeWharton: Wenn ich eine benutzerdefinierte Animation auf jedes Element von a anwenden möchte ListView, schlagen Sie vor, Folgendes zu tun : private static ViewHolder { private ImageView imageView; private Target target = new Target() { public void onBitmapLoaded() { // do animation on imageView } } }?
mbmc
@JakeWharton erklärt es hier github.com/square/picasso/issues/308 im letzten Kommentar.
toobsco42
8
onBitmapLoaded nicht zum ersten Mal nach onPrepareLoad anrufen
Amit Thaper
Danke für das Beispiel. Ich vermisse nur einen Superanruf in onDestroy.
Ben Groot
68

von hier genommen :

Picasso.with(this)
    .load(url)
    .into(new Target() {
        @Override
        public void onBitmapLoaded (final Bitmap bitmap, Picasso.LoadedFrom from){
            /* Save the bitmap or do something with it here */

            //Set it in the ImageView
            theView.setImageBitmap(bitmap); 
        }
});

Aktualisiert (04. Mai 2016):

            Picasso.with(this)
                .load(youUrl)
                .into(new Target() {
                    @Override
                    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

                    }

                    @Override
                    public void onBitmapFailed(Drawable errorDrawable) {

                    }

                    @Override
                    public void onPrepareLoad(Drawable placeHolderDrawable) {

                    }
                });

Aktualisiert (22. November 2016)

oder verwenden Sie eine starke Referenz Target, damit kein Müll gesammelt wird

Target target = new Target() {
            @Override
            public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {

            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {

            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {

            }
        };


void foo() {
        Picasso.with(getContext()).load(getUrl()).into(target);
}

Kotlin

object: com.squareup.picasso.Target {
                  override fun onBitmapFailed(e: java.lang.Exception?, errorDrawable: Drawable?) {
                    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
                }

                  override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
                    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
                  }

                  override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {

                  }
                }
AbdulMomen عبدالمؤمن
quelle
6
anonymes Ziel könnte Müll gesammelt werden
Steve M
@SteveM Und was würde das tun? Anfrage abbrechen? Das Laden der Bitmap verhindern?
Nurettin
@nurettin Ich gehe davon aus, dass Picasso (oder Glide) einen schwachen Bezug zum Ziel hat. Wenn es also anonym ist, gibt es keine feste Referenz und es ist anfällig für GC. Wenn Picasso die Referenz überprüft, ist sie null, sodass der Rückruf nicht aufgerufen wird.
Steve M
Diese Lösung ist also wirklich schlecht, da der GC gelegentlich zwischen dem Laden und Aufrufen des Rückrufs ausgeführt wird und dazu führt, dass der Rückruf nicht aufgerufen wird.
Steve M
@SteveM Ich hoste eine Anwendung auf Google Play, die während des Bildlaufs der Listenansichten eine Reihe von Symbolen mit mindestens 2000 Benutzern lädt und einige der Symbole mit dieser Methode skaliert, aber ich habe keine Beschwerde gesehen, Kommentar ( Ich bekomme viele) oder Absturzberichte über nicht geladene Symbole. Zumindest anekdotisch wird aus irgendeinem Grund kein Müll gesammelt.
Nurettin
7

Was kann einfacher sein als das nächste:

val url: String = "https://...."
val bitmap: Bitmap = Picasso.with(context).load(url).get()

Sollte nicht vom Haupt-Thread aufgerufen werden!

oder mit RxJava 2:

fun getBitmapSingle(picasso: Picasso, url: String): Single<Bitmap> = Single.create {
    try {
        if (!it.isDisposed) {
            val bitmap: Bitmap = picasso.load(url).get()
            it.onSuccess(bitmap)
        }
    } catch (e: Throwable) {
        it.onError(e)
    }
}

Bitmap abrufen:

getBitmapSingle(Picasso.with(context), "https:/...")
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({ bitmap ->
                // val drawable = BitmapDrawable(context, bitmap)
                }, Throwable::printStackTrace)

Ich habe Picasso v.2.5.2 verwendet

Oleg Tarashkevich
quelle
2

Ich dachte, vielleicht möchten einige von Ihnen eine RxJava-Version der obigen Antwort ... Hier ist es:

    public static Observable<Bitmap> loadBitmap(Picasso picasso, String imageUrl) {
    return Observable.create(new Observable.OnSubscribe<Bitmap>() {
        @Override
        public void call(Subscriber<? super Bitmap> subscriber) {
            Target target = new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    subscriber.onNext(bitmap);
                    subscriber.onCompleted();
                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                    subscriber.onError(new Exception("failed to load " + imageUrl));
                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                }
            };
            subscriber.add(new Subscription() {
                private boolean unSubscribed;

                @Override
                public void unsubscribe() {
                    picasso.cancelRequest(target);
                    unSubscribed = true;
                }

                @Override
                public boolean isUnsubscribed() {
                    return unSubscribed;
                }
            });
            picasso.load(imageUrl).into(target);
        }
    });
}

PS Speichern Sie beim Abonnieren die Abonnementreferenz für Ihre Aktivität. Andernfalls wird das Ziel GC-geprüft, bevor Sie eine Antwort erhalten ...

Sergey Aldoukhov
quelle
Scheint, dass dieser Code jetzt nicht mit dem neuesten RxAndroid
Pavandroid funktioniert