Используйте Picasso, чтобы получить обратный вызов с помощью Bitmap

83

Я использую Picasso для загрузки изображений в свое приложение.

Я в ситуации, когда мне нужно получить доступ к Bitmapпервому, прежде чем он загрузится в ImageView. Наличие Downloader.Responseкласса предполагает, что это возможно, но я не могу найти никаких примеров использования. Я не хочу писать еще больше кода для асинхронной обработки этого конкретного случая, если это возможно с Пикассо.

Кто-нибудь может показать мне, как это сделать?

Стив М
источник

Ответы:

173

Нашел ответ на github, если кому-то интересно:

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

В сообщении рекомендуется не использовать анонимный обратный вызов, а вместо этого использовать переменную экземпляра для цели.

Стив М
источник
30
В идеале вы должны реализовать Targetобъект представления или держателя представления напрямую. Если вы этого не делаете, вам нужно поддерживать надежную ссылку на экземпляр где-нибудь, иначе он будет собираться сборщиком мусора.
Джейк Уортон
@JakeWharton: если я хочу , чтобы применить пользовательскую анимацию на каждую физическую единицу ListView, вы предлагаете сделать что - то вроде: private static ViewHolder { private ImageView imageView; private Target target = new Target() { public void onBitmapLoaded() { // do animation on imageView } } }?
mbmc
@JakeWharton объясняет это здесь github.com/square/picasso/issues/308 в последнем комментарии.
toobsco42
8
onBitmapLoaded не вызывается в первый раз после onPrepareLoad
Амит Тапер
Спасибо за пример. Мне не хватает только супер звонка в onDestroy.
Бен Гроот
71

взято отсюда :

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

Обновлено (4 мая 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) {

                    }
                });

Обновлено (22 ноября 2016 г.)

или используя сильную ссылку, Targetчтобы он не собирался сборщиком мусора

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

Котлин

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

                  }
                }
AbdulMomen عبدالمؤمن
источник
6
анонимная цель может быть собрана мусором
Стив М.
@SteveM И что это будет делать? Отменить заявку? Предотвратить загрузку растрового изображения?
nurettin
@nurettin Я предполагаю, что у Picasso (или Glide) есть какая-то слабая ссылка на цель. Так что, если это анонимно, жесткой ссылки нет и он уязвим для GC. Когда Пикассо проверяет ссылку, она будет нулевой, поэтому обратный вызов не будет вызываться.
Steve M
Итак, это решение действительно плохое, потому что GC может иногда запускаться между загрузкой и вызовом обратного вызова и вызывать обратный вызов.
Steve M
@SteveM Я размещаю приложение в Google Play, которое загружает кучу значков во время прокрутки его списков с минимум 2000 пользователей в любом случае, масштабируя некоторые значки с помощью этого метода, но я не видел никаких жалоб, комментарий ( Я получаю много) или отчет о сбое о том, что значки не загружаются. Так что, по крайней мере, анекдотично, по какой-то причине он не собирает мусор.
nurettin
7

Что может быть проще следующего:

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

Должен вызываться не из основного потока!

или с 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)
    }
}

Получить растровое изображение:

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

Я использовал Picasso v.2.5.2

Олег Тарашкевич
источник
2

Я подумал, может быть, некоторым из вас нужна RxJava-версия приведенного выше ответа ... Вот она:

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

PS При подписке сохраните ссылку на подписку в своей деятельности, иначе цель будет GC'd до того, как вы получите ответ ...

Сергей Алдухов
источник
Кажется, этот код сейчас не работает с последней версией RxAndroid
Pavandroid