web-dev-qa-db-fra.com

onBitmapLoaded de l'objet cible non appelé lors du premier chargement

Dans ma fonction:

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

Le onBitmapLoaded () n'est jamais appelé la première fois que je charge des images. J'ai lu des sujets comme https://github.com/square/picasso/issues/39 qui recommandent d'utiliser la méthode fetch (Target t) (cela semble être un problème de référence faible. ..), mais cette fonction n’est pas disponible dans la dernière version de picasso (2.3.2). J'ai seulement une méthode fetch (), mais je ne peux pas utiliser (mytarget) en même temps

Pourriez-vous m'expliquer comment utiliser fetch () avec une cible personnalisée, s'il vous plaît? Je vous remercie.

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

120
psv

Comme l'ont noté les autres répondants (@lukas et @mradzinski), Picasso ne conserve qu'une faible référence à l'objet Target. Bien que vous puissiez stocker une référence forte Target dans l'une de vos classes, cela peut toujours être problématique si Target fait référence à View de quelque manière que ce soit, car vous aurez également gardez également une référence forte à ce View (ce qui est une des choses que Picasso vous a explicitement évité).

Si vous êtes dans cette situation, je vous conseillerais de taguer le Target au View:

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

Cette approche a l'avantage de laisser Picasso gérer tout pour vous. Il gérera les objets WeakReference pour chacune de vos vues - dès que vous n'en aurez plus besoin, quel que soit le traitement de Target, l'image sera également publiée, vous évitant ainsi des fuites de mémoire. en raison de cibles à long terme, mais votre cible durera tant que sa vue restera active.

234
wrb

Picasso ne contient pas de référence forte à l’objet Cible. Il est donc collecté et l’onBitmapLoaded n’est pas appelé.

La solution est simple, il suffit de faire une référence forte à la cible.

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

Si j'avais ImageView, je ferais simplement ceci: imageView.setTag (cible);

J'utilise la solution suivante pour charger des bitmaps dans les notifications, je n'ai donc besoin que de bitmap.

Alors, créez Set qui stockera les objets cibles et les supprimera à la fin du chargement.

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

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

            }
        });
12
Raghav Satyadev

Voici la solution pour ceux qui n'utilisent pas de vue. Cette méthode d'assistance utilise une liste pour stocker temporairement l'objet cible jusqu'à ce qu'un résultat soit renvoyé afin qu'il ne soit pas généré:

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(Drawable errorDrawable) {
            targets.clear();
            callback.onFailure(null);
        }

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

Comme @lukas a dit (et citant), Picasso ne détient pas de forte référence à l'objet cible. Pour éviter la récupération de place, vous devez tenir une référence forte à l'objet.

A propos de la méthode fetch (). Il est assez clair dans la documentation que fetch () ne doit pas être utilisé avec une ImageView ni une cible, c'est juste pour "réchauffer" le cache et rien d'autre, pour que vous ne puissiez pas l'utiliser comme vous le feriez vouloir.

Je vous recommande de tenir une référence forte comme @lukas a expliqué, cela devrait fonctionner. Sinon, ouvrez un nouveau numéro sur la page GitHub du projet.

3
mradzinski

J'ai rencontré un problème similaire et le maintien de la référence à la cible n'a pas aidé du tout, alors j'ai utilisé le code suivant qui renvoie un bitmap:


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

côté négatif -> il n'y a pas de rappel et vous ne pouvez pas appeler cette fonction sur le thread principal, vous devez l'exécuter sur un thread d'arrière-plan, comme dans l'exemple suivant:


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

Une autre chose qui fonctionne beaucoup mieux est simplement d'utiliser Glide!

Je devais utiliser les deux car l'objectif de mon projet était d'utiliser deux api de téléchargement d'images différentes pour montrer une galerie d'images et permettre à l'utilisateur de choisir quelle api utiliser.

Je dois dire que les résultats m'ont impressionné. L'API de Glide a parfaitement fonctionné (la cible de Glide n'a pas de références faibles). on dirait qu'aujourd'hui ça va changer ^^).

3
Roee