onBitmapLoaded del objeto Target no llamado en la primera carga

126

En mi funcion:

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 () nunca se llama la primera vez que cargo imágenes. He leído algún tema como https://github.com/square/picasso/issues/39 que recomienda usar el método fetch (Target t) (parece ser un problema de referencia débil ...), pero esta función no está disponible en la última versión de picasso (2.3.2). Solo tengo un método fetch (), pero no puedo usar into (mytarget) al mismo tiempo

¿Podría explicarme cómo usar fetch () con un Target personalizado, por favor? Gracias.

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

psv
fuente
1
asegúrese de usar okhttp 2.0.0, encuentro el mismo problema cuando uso Picasso 2.3.2 con Okhttp 1.6.0
hakim
github.com/square/okhttp afaik, es obligatorio si está utilizando Picasso 2.3.2 para incluir la biblioteca okhttp (y okio). ¿Estás usando eclipse o android studio?
hakim
Estoy usando IntelliJ. He visto mis dependencias de gradle, no vi okhttp ... Picasso parece funcionar sin él
psv
@psv ¿cómo implementó la siguiente solución con los marcadores?
Mustafa Güven

Respuestas:

247

Como lo señalaron los otros encuestados (@lukas y @mradzinski), Picasso solo mantiene una referencia débil al Targetobjeto. Si bien puede almacenar una referencia fuerte Targeten una de sus clases, esto puede ser problemático si las Targetreferencias Viewde alguna manera, ya que efectivamente también mantendrá una referencia fuerte a eso View(que es una de las cosas que Picasso explícitamente te ayuda a evitar).

Si se encuentra en esta situación, le recomiendo etiquetar Targeta View:

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

Este enfoque tiene la ventaja de permitir que Picasso se encargue de todo por usted. Administrará los WeakReferenceobjetos para cada una de sus vistas: tan pronto como ya no sea necesario, cualquier Targetproceso que procese la imagen también se liberará, por lo que no estará atascado con pérdidas de memoria debido a objetivos de larga duración, pero su objetivo durará mientras su vista esté viva.

wrb
fuente
15
Me salvó el día Gracias.
cy198706
24
No tengo una vista de imagen, ¿cómo puedo resolver este problema entonces? Cuando se trata de este tipo de situaciones, el GC es su peor enemigo
tim687
3
Incluso puede almacenarlo en una ArrayList <Target> y funcionará, solo recuerde borrar esa matriz :-)
Oliver Dixon
2
En onBitmapLoaded y onBitmapFailed, también estoy haciendo imageView.setTag (nulo) después de procesar el mapa de bits. ¿No es necesario?
Jaguar
1
¡Gracias! Acabo de salvar mi vida :)
yusufiga
55

Picasso no tiene una referencia fuerte al objeto Target, por lo tanto, se está recolectando basura y onBitmapLoadedno se llama.

La solución es bastante simple, solo haga una fuerte referencia al 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);
   }
}      
lukas
fuente
2
O haga su Viewimplemento Target.
dnkoutso
en los documentos, dice que tienes que anular Object.equals(Object)y Object.hashCode()métodos. ¿Tienes una muestra de trabajo?
chip
donde esta escrito Todavía tengo mi problema incluso haciendo una fuerte referencia a mi Target ().
psv
Ahora he instalado okHttp, es un poco más rápido de cargar pero todavía tengo el mismo problema en el primer lanzamiento. Algunas ideas ?
psv
@psv: ¿Resolviste el primer problema de lanzamiento de picasso? ¿Tengo el mismo problema? Si has resuelto, ¿cómo lo resolviste?
TheDevMan
25

Si tuviera ImageView, simplemente haría así: imageView.setTag (target);

Uso la siguiente solución para cargar mapas de bits en las notificaciones, por lo que solo necesito un mapa de bits.

Por lo tanto, crear Set bruja almacenará objetos Target y los eliminará al finalizar la carga.

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) {

        }
    }
Flinbor
fuente
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() {

            }
        });
Raghav Satyadev
fuente
1
También resolvió mi problema. Quería usarlo con la notificación. a veces la imagen se descargaba con Target y otras no. pero después de usar ImageView pude cargar imágenes cada vez
Raveesh GS
1
en mi caso, excepto todos, ¡esta fue la mejor solución!
Noor Hossain
4

Aquí está la solución para aquellos que no están usando una vista. Este método auxiliar utiliza una lista para almacenar temporalmente el objeto de destino hasta que se devuelva un resultado para que no se gc'd:

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);
}
DroidT
fuente
3

Como dijo @lukas (y citando), Picasso no tiene una fuerte referencia al objeto Target. Para evitar la recolección de basura, debe tener una fuerte referencia al objeto.

Sobre el método fetch (). Está bastante claro en la documentación que fetch () no se debe usar con un ImageView ni un Target, es solo para "calentar" el caché y nada más, por lo que no podrá usarlo de la manera que usted querer.

Te recomiendo que tengas una referencia fuerte como explicó @lukas, debería funcionar. De lo contrario, abra un nuevo problema en la página de GitHub del proyecto.

mradzinski
fuente
3

Encontré un problema similar y mantener la referencia al objetivo no ayudó en absoluto, así que utilicé el siguiente código que devuelve un mapa de bits:


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

en el lado negativo -> no hay devolución de llamada y no puede llamar a esta función en el hilo principal, debe ejecutar esta función en un hilo de fondo como en el siguiente ejemplo:


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

¡Otra cosa que funciona mucho mejor es usar Glide!

Necesitaba usar ambos, ya que el propósito de mi proyecto era usar 2 API de descarga de imágenes diferentes para mostrar una galería de imágenes y darle al usuario la capacidad de elegir qué API usar.

Tengo que decir que me sorprendieron los resultados, la API de Glide funcionó a la perfección en todos los aspectos (el objetivo de Glide no tiene una referencia débil) mientras Picasso me dio un infierno (esa fue la primera vez que usé Glide, por lo general usé Picasso hasta ahora, Parece que hoy va a cambiar ^^).

Roee
fuente
0

Me enfrenté al mismo problema, pero cuando cambio la dependencia como se menciona a continuación, ahora funciona correctamente.

 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'
khushbu
fuente