88

I'm using Picasso to download images for my app.

I'm in a situation where I need to access the Bitmap first before it's loaded into the ImageView. The presence of the Downloader.Response class seems to suggest this is possible, but I can't find any use examples. I don't want to write a bunch more code to asynchronously handle this one particular case if it's possible to do with Picasso.

Can anyone show me how to do it?

mixel
  • 25,177
  • 13
  • 126
  • 165
Steve M
  • 9,296
  • 11
  • 49
  • 98

4 Answers4

177

Found the answer on github in case anyone is wondering:

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

The post recommends not using an anonymous callback, and instead using an instance variable for target.

GuilhE
  • 11,591
  • 16
  • 75
  • 116
Steve M
  • 9,296
  • 11
  • 49
  • 98
  • 31
    Ideally you'd implement `Target` on a view or view holder object directly. If you aren't doing this, you need to maintain a strong reference to the instance somewhere otherwise it will get garbage collected. – Jake Wharton Nov 25 '13 at 16:42
  • @JakeWharton: if I want to apply a custom animation on every item of a `ListView`, you suggest to do something like: `private static ViewHolder { private ImageView imageView; private Target target = new Target() { public void onBitmapLoaded() { // do animation on imageView } } }`? – mbmc Mar 27 '14 at 06:17
  • @JakeWharton explains it here https://github.com/square/picasso/issues/308 in the last comment. – Etienne Lawlor May 05 '14 at 22:30
  • 8
    onBitmapLoaded not call at first time after onPrepareLoad – Amit Thaper Oct 01 '14 at 10:30
  • Thanks for the example. I'm only missing a super call in onDestroy. – Ben Groot Oct 13 '14 at 13:22
  • using a Target bypasses the nice alpha animation done by the `PicassoDrawable`. I'd like to keep that behaviour and get the bitmap at the same time to add an overlay with a color generated from the `Palette` api. Any way to do that ? – mbonnin May 24 '17 at 15:34
  • I want to use Target in Kotlin but it's giving error ..."This type is final, so it can not be inherited from" – Vivek Pratap Singh Jun 25 '19 at 07:13
  • How to do this on the main thread? If we use another thread, it takes some time. and the purpose of the process gets wasted. There is a continuous process after loading into a bitmap. anyone can help on this? – Silambarasan Nov 23 '19 at 08:35
  • Image not load first time. Can anyone help? – Neal Sep 30 '20 at 10:34
74

taken from here:

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

Updated (May 04, 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) {

                    }
                });

Updated (November 22, 2016)

or using a strong reference for Target so that it wont be garbage collected

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

                  }
                }
Salih
  • 171
  • 1
  • 12
  • 6
    anonymous Target could be garbage collected – Steve M Apr 03 '16 at 16:25
  • @SteveM And what would that do? Cancel the request? Prevent bitmap from being loaded? – nurettin Nov 22 '16 at 18:27
  • @nurettin I'm assuming Picasso (or Glide) has some sort of weak reference to the target. So if it's anonymous there is no hard reference and it's vulnerable to GC. When Picasso checks the reference it will be null so the callback won't be called. – Steve M Nov 22 '16 at 19:14
  • So this solution is really bad because the GC may occasionally run in between the loading and calling of the callback and cause callback to not be called. – Steve M Nov 22 '16 at 19:20
  • @SteveM I host an application on google play that loads a bunch of icons during the scroll of its listviews with at least 2000 users at any instance, scaling some of the icons using this method, but I haven't seen any complaint, comment (I get lots) or crash report about icons not loading. So at least anecdotally, it isn't getting garbage collected for some reason. – nurettin Nov 22 '16 at 19:46
  • @nurettin I guess you are lucky. The author of Picasso (Jack Wharton) specifically warns against it as a comment to the accepted answer. It is also warned against in the Glide documentation which is a very similar library. https://futurestud.io/tutorials/glide-callbacks-simpletarget-and-viewtarget-for-custom-view-classes under "Pay Attention with Targets". – Steve M Nov 22 '16 at 22:48
  • @SteveM Thanks for making the point, I updated the application just in case. – nurettin Nov 23 '16 at 05:56
  • For the Kotlin, it needs to be 'onBitmapFailed(e: Exception, errorDrawable: Drawable?)' –  Aug 31 '18 at 10:17
11

What can be easy than next:

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

Should be called from not the main thread!

or with 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)
    }
}

Retrieve Bitmap:

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

I used Picasso v.2.5.2

Prilaga
  • 818
  • 10
  • 18
2

I thought maybe some of you would like an RxJava version of the above answer... Here it is:

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

P.S. When subscribing, store the subscription reference on your activity, otherwise, target will be GC'd before you receive a response...

Sergey Aldoukhov
  • 22,316
  • 18
  • 72
  • 99