136

In my function :

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

The onBitmapLoaded() is never called the first time I load pictures. I've read some topic like https://github.com/square/picasso/issues/39 which recommand to use fetch(Target t) method (it seems to be a problem of weak reference...), but this function is not available in the last release of picasso (2.3.2). I've only a fetch() method, but I cannot use into(mytarget) at the same time

Could you explain me how to use fetch() with a custom Target please ? Thank you.

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

psv
  • 3,147
  • 5
  • 32
  • 67
  • 1
    make sure to use okhttp 2.0.0, I encounter same issue when using Picasso 2.3.2 with Okhttp 1.6.0 – hakim Jun 26 '14 at 10:17
  • https://github.com/square/okhttp afaik, it is mandatory if you're using Picasso 2.3.2 to include okhttp (and okio) library. are you using eclipse or android studio ? – hakim Jun 27 '14 at 04:35
  • I'm using IntelliJ. I've seen my gradle dependencies, I didn't see okhttp... Picasso seems to work without it – psv Jun 27 '14 at 08:30
  • @psv how did you implement below solution with the markers? – Mustafa Güven Jan 12 '16 at 15:01

8 Answers8

262

As noted by the other respondents (@lukas and @mradzinski), Picasso only keeps a weak reference to the Target object. While you can store a strong reference Target in one of your classes, this can still be problematic if the Target references a View in any manner, since you'll effectively also be keeping a strong reference to that View as well (which is one of the things that Picasso explicitly helps you avoid).

If you are in this situation, I'd recommend tagging the Target to the View:

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

This approach has the benefit of letting Picasso handle everything for you. It will manage the WeakReference objects for each of your views - as soon as one is no longer needed, whatever Target processing the image will also be released, so you're not stuck with memory leaks due to long-lived targets, but your Target will last as long as its view is alive.

wrb
  • 3,006
  • 1
  • 12
  • 7
  • YES! I chose to create a parallel list of target (List) to keep all the target objects, but your solution is better. The target "will last as long as it's view is alive". Thanks. – psv Nov 15 '14 at 14:37
  • 26
    I don't have an image view, how can I solve this problem then? When dealing with this kind of situations, the gc is your worse enemy – tim687 Aug 11 '15 at 18:17
  • 4
    You can even store it in an ArrayList and it will work, just remember to clear that arraylist :-) – Oliver Dixon Feb 22 '16 at 17:32
  • 2
    In onBitmapLoaded and onBitmapFailed, I am also doing imageView.setTag(null) after processing the bitmap. Is it not needed ? – dev Jul 30 '16 at 03:37
  • 1
    Shouldn't we nullify the tag in `onBitmapLoaded` / `onBitmapFailed` ? – WindRider May 26 '17 at 07:51
  • @tim687 I don't have a view either so i have nothing to tag my target to. I know this is old, but did you find a solution? – Cody Dec 10 '19 at 17:22
64

Picasso does not hold a strong reference to the Target object, thus it's being garbage collected and onBitmapLoaded is not called.

The solution is quite simple, just make a strong reference to the 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);
   }
}      
Αntonis Papadakis
  • 1,210
  • 1
  • 12
  • 22
lukasz
  • 3,121
  • 1
  • 21
  • 20
  • 3
    Or make your `View` implement `Target`. – dnkoutso Jul 11 '14 at 23:00
  • in the docs, it says you have to override `Object.equals(Object)` and `Object.hashCode()` methods. do you have a working sample? – chip Jul 21 '14 at 06:57
  • where is it written ? I still have my problem even by making a strong reference to my Target(). – psv Jul 29 '14 at 13:04
  • I have now installed okHttp, it's a bit faster to load but I still have the same problem at the first launch. Any ideas ? – psv Jul 29 '14 at 16:09
  • @psv: Did you get to solve picasso first launch issue? I have the same problem? If you have solved how did you solve it? – TheDevMan Sep 16 '14 at 04:53
  • @TheDevMan, nope it's not solved. Workaround solution I used : Save each target object in an ArrayList to avoid garbage collection. It's "dirty" and reduce performances but I don't have any other solution. – psv Sep 25 '14 at 11:57
27

If I had ImageView I would simple make like this: imageView.setTag(target);

I use next solution for loading Bitmaps into notifications, so I need only bitmap.

So create Set witch will store Target objects and remove them on finish loading.

final Set<Target> protectedFromGarbageCollectorTargets = new HashSet<>();

private void loadBitmap(String url) {
   Target bitmapTarget = new BitmapTarget();
   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) {
 
        }
    }
Oleksandr B
  • 3,400
  • 1
  • 25
  • 28
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
  • 728
  • 2
  • 11
  • 38
  • 1
    It's solved my problem too. i wanted use it with Notification. sometimes image was downloading with Target and sometimes not. but after using ImageView i was able to load images everytime – Raveesh G S Mar 31 '19 at 13:04
  • 1
    in my case, except all, this was the best solution! – Noor Hossain Mar 25 '20 at 13:43
  • @RaveeshGS notification builder in android uses setLargeIcon(Bitmap) to set Image. I don't know how you use ImageView instead of setLargeIcon !? – NimaAzhd Aug 31 '22 at 16:27
  • @NimaAzhd they are using imageview to just download the bitmap. After downloading you can use it wherever you want. – Raghav Satyadev Sep 02 '22 at 05:24
5

I encountered similar issue and holding reference to the target didn't helped at all so I used the following code which returns a Bitmap:


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

on the down side -> there's no callback and you can't call this function on the main thread, you have to run this function on a background thread as in the following example:


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

Another thing that works a whole lot better is just using Glide!

I needed to use both of them since the purpose of my project was to use 2 different image downloading api's to show an images gallery and to give the user the ability to choose which api to use.

I have to say, I was amazed by the results, Glide's api worked flawlessly in every aspect (Glide's target doesn't have weak reference) wile Picasso gave me hell (that was my first time using Glide, I usually used Picasso so far, seems like today it's gonna change ^^ ).

Roee
  • 1,155
  • 3
  • 15
  • 24
5

Here is the solution for those who aren't using a view. This helper method uses a list to temporarily store the target object until a result is returned so that it isn't 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);
}
4levels
  • 3,134
  • 1
  • 23
  • 22
DroidT
  • 3,168
  • 3
  • 31
  • 29
3

Like @lukas said (and quoting), Picasso does not hold a strong reference to the Target object. To avoid garbage collection you must hold a strong reference to the object.

About fetch() method. It's pretty clear in the documentation that fetch() is not to be used with an ImageView nor a Target, it's just to "warm" up the cache and nothing else, so you're not going to be able to use it the way you want.

I recommend you holding a strong reference like @lukas explained, it should work. If not please open up a new issue on the GitHub page of the project.

mradzinski
  • 614
  • 1
  • 8
  • 21
0

I had faced same issue but when I change the dependency as below mentioned, It works properly now.

 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
  • 382
  • 1
  • 3
  • 15