Utilice Picasso para obtener una devolución de llamada con un mapa de bits

83

Estoy usando Picasso para descargar imágenes para mi aplicación.

Estoy en una situación en la que necesito acceder al Bitmapprimero antes de que se cargue en ImageView. La presencia de la Downloader.Responseclase parece sugerir que esto es posible, pero no puedo encontrar ningún ejemplo de uso. No quiero escribir mucho más código para manejar de forma asincrónica este caso particular si es posible hacerlo con Picasso.

¿Alguien puede mostrarme cómo hacerlo?

Steve M
fuente

Respuestas:

172

Encontré la respuesta en github en caso de que alguien se lo pregunte:

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();
}

La publicación recomienda no usar una devolución de llamada anónima y, en su lugar, usar una variable de instancia para el objetivo.

Steve M
fuente
30
Lo ideal sería implementarlo Targeten una vista o en un objeto de titular de vista directamente. Si no está haciendo esto, debe mantener una fuerte referencia a la instancia en algún lugar, de lo contrario, se recolectará la basura.
Jake Wharton
@JakeWharton: si quiero aplicar una animación personalizada en cada elemento de un ListView, sugieres hacer algo como private static ViewHolder { private ImageView imageView; private Target target = new Target() { public void onBitmapLoaded() { // do animation on imageView } } }:?
mbmc
@JakeWharton lo explica aquí github.com/square/picasso/issues/308 en el último comentario.
toobsco42
8
onBitmapLoaded no se llama por primera vez después de onPrepareLoad
Amit Thaper
Gracias por el ejemplo. Solo me falta una súper llamada en onDestroy.
Ben Groot
69

tomado de aquí :

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); 
        }
});

Actualizado (4 de mayo de 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) {

                    }
                });

Actualizado (22 de noviembre de 2016)

o usando una referencia fuerte para Targetque no se recolecte basura

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 عبدالمؤمن
fuente
6
Target anónimo podría ser recolectado de basura
Steve M
@SteveM ¿Y qué haría eso? ¿Cancelar la solicitud? ¿Evitar que se cargue el mapa de bits?
nurettin
@nurettin Supongo que Picasso (o Glide) tiene algún tipo de referencia débil al objetivo. Entonces, si es anónimo, no hay una referencia estricta y es vulnerable a GC. Cuando Picasso verifique la referencia, será nula, por lo que no se llamará a la devolución de llamada.
Steve M
Por lo tanto, esta solución es realmente mala porque el GC ocasionalmente puede ejecutarse entre la carga y la llamada de la devolución de llamada y hacer que no se llame a la devolución de llamada.
Steve M
@SteveM Alojo una aplicación en Google Play que carga un montón de íconos durante el desplazamiento de su lista de vistas con al menos 2000 usuarios en cualquier instancia, escalando algunos de los íconos usando este método, pero no he visto ninguna queja, comentario ( Recibo muchos) o un informe de bloqueo sobre los íconos que no se cargan. Entonces, al menos de manera anecdótica, no se está recolectando basura por alguna razón.
nurettin
7

Qué puede ser más fácil que el siguiente:

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

¡Debería ser llamado no desde el hilo principal!

o con 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)
    }
}

Recuperar mapa de bits:

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

Usé Picasso v.2.5.2

Oleg Tarashkevich
fuente
2

Pensé que tal vez a algunos de ustedes les gustaría una versión RxJava de la respuesta anterior ... Aquí está:

    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);
        }
    });
}

PD Al suscribirse, almacene la referencia de suscripción en su actividad; de lo contrario, el objetivo será GC antes de recibir una respuesta ...

Sergey Aldoukhov
fuente
Parece que este código no funciona ahora con el último RxAndroid
Pavandroid