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

126

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

OnBitmapLoaded () n'est jamais appelé la première fois que je charge des images. J'ai lu des rubriques 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). Je n'ai qu'une méthode fetch (), mais je ne peux pas utiliser dans (mytarget) en même temps

Pouvez-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--

psv
la source
1
assurez-vous d'utiliser okhttp 2.0.0, je rencontre le même problème lors de l'utilisation de Picasso 2.3.2 avec Okhttp 1.6.0
hakim
github.com/square/okhttp afaik, il est obligatoire si vous utilisez Picasso 2.3.2 d'inclure la bibliothèque okhttp (et okio). utilisez-vous eclipse ou android studio?
hakim
J'utilise IntelliJ. J'ai vu mes dépendances gradle, je n'ai pas vu okhttp ... Picasso semble fonctionner sans lui
psv
@psv comment avez-vous implémenté la solution ci-dessous avec les marqueurs?
Mustafa Güven

Réponses:

247

Comme le notent les autres répondants (@lukas et @mradzinski), Picasso ne garde qu'une faible référence à l' Targetobjet. Bien que vous puissiez stocker une référence forte Targetdans l'une de vos classes, cela peut toujours être problématique si les Targetréférences sont d'une Viewmanière ou d'une autre, car vous conserverez également une référence forte à cela View(ce qui est l'une des choses que Picasso vous aide explicitement à éviter).

Si vous êtes dans cette situation, je vous recommande de marquer le Targetsur 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 s'occuper de tout pour vous. Il gérera les WeakReferenceobjets pour chacune de vos vues - dès que vous n'en Targetaurez plus besoin, quel que soit le traitement, l'image sera également libérée, vous n'êtes donc pas coincé avec des fuites de mémoire dues à des cibles de longue durée, mais votre cible durera tant que sa vue est vivante.

wrb
la source
15
J'ai sauvé ma journée. Merci.
cy198706
24
Je n'ai pas de vue d'image, comment puis-je résoudre ce problème? Lorsque vous
faites
3
Vous pouvez même le stocker dans une ArrayList <Target> et cela fonctionnera, rappelez-vous simplement d'effacer cette arraylist :-)
Oliver Dixon
2
Dans onBitmapLoaded et onBitmapFailed, je fais également imageView.setTag (null) après le traitement du bitmap. N'est-ce pas nécessaire?
Jaguar
1
Je vous remercie! Je viens de sauver ma vie :)
yusufiga
55

Picasso ne contient pas de référence forte à l'objet Target, il est donc en cours de récupération de place et onBitmapLoadedn'est pas appelé.

La solution est assez simple, il suffit de faire une forte référence au 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
la source
2
Ou faites votre Viewoutil Target.
dnkoutso du
dans la documentation, il est dit que vous devez remplacer Object.equals(Object)et Object.hashCode()méthodes. avez-vous un échantillon de travail?
puce
où est-il écrit? J'ai toujours mon problème même en faisant une référence forte à ma cible ().
psv
J'ai maintenant installé okHttp, c'est un peu plus rapide à charger mais j'ai toujours le même problème au premier lancement. Des idées ?
psv
@psv: Avez-vous résolu le problème du premier lancement de Picasso? J'ai le même problème? Si vous avez résolu, comment l'avez-vous résolu?
TheDevMan
25

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

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

Alors créez Set witch stockera les objets Target 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) {

        }
    }
Flinbor
la source
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
la source
1
Cela a aussi résolu mon problème. je voulais l'utiliser avec Notification. parfois l'image était téléchargée avec Target et parfois non. mais après avoir utilisé ImageView, j'ai pu charger des images à chaque fois
Raveesh GS
1
dans mon cas, sauf tout, c'était la meilleure solution!
Noor Hossain
4

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 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
la source
3

Comme @lukas l'a dit (et citant), Picasso ne contient pas de référence forte à l'objet Target. Pour éviter le garbage collection, vous devez contenir une référence forte à l'objet.

À 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, donc vous ne pourrez pas l'utiliser comme vous vouloir.

Je vous recommande de tenir une référence forte comme @lukas l'a expliqué, cela devrait fonctionner. Sinon, veuillez ouvrir un nouveau problème sur la page GitHub du projet.

mradzinski
la source
3

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


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

en revanche -> il n'y a pas de rappel et vous ne pouvez pas appeler cette fonction sur le thread principal, vous devez exécuter cette fonction 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!

J'avais besoin de les utiliser tous les deux car le but de mon projet était d'utiliser 2 API de téléchargement d'images différentes pour afficher une galerie d'images et pour donner à l'utilisateur la possibilité de choisir l'API à utiliser.

Je dois dire que j'ai été étonné par les résultats, l'api de Glide fonctionnait parfaitement dans tous les aspects (la cible de Glide n'a pas de référence faible) alors que Picasso m'a donné l'enfer (c'était la première fois que j'utilisais Glide, j'utilisais habituellement Picasso jusqu'à présent, on dirait qu'aujourd'hui ça va changer ^^).

Roee
la source
0

J'avais rencontré le même problème, mais lorsque je change la dépendance comme mentionné ci-dessous, cela fonctionne correctement maintenant.

 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
la source