onBitmapLoaded целевого объекта, не вызываемого при первой загрузке

В моей функции:

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() никогда не вызывается при первой загрузке изображений. Я прочитал некоторые темы, такие как https://github.com/square/picasso/issues/39, которые рекомендуют использовать метод fetch(Target t) (похоже, это проблема слабой ссылки...), но эта функция недоступна в последней версии picasso (2.3.2). У меня есть только метод fetch(), но я не могу одновременно использовать into(mytarget)

Не могли бы вы объяснить мне, как использовать fetch() с пользовательской целью, пожалуйста? Спасибо.

Документ: http://square.github.io/picasso/javadoc/com/squareup/picasso/RequestCreator.html#fetch--


person psv    schedule 12.06.2014    source источник
comment
обязательно используйте okhttp 2.0.0, я сталкиваюсь с той же проблемой при использовании Picasso 2.3.2 с Okhttp 1.6.0   -  person hakim    schedule 26.06.2014
comment
https://github.com/square/okhttp на самом деле, если вы используете Picasso 2.3.2 для включить библиотеку okhttp (и okio). вы используете eclipse или android studio?   -  person hakim    schedule 27.06.2014
comment
Я использую IntelliJ. Я видел свои зависимости gradle, я не видел okhttp... Пикассо, кажется, работает без него   -  person psv    schedule 27.06.2014
comment
@psv, как вы реализовали приведенное ниже решение с маркерами?   -  person Mustafa Güven    schedule 12.01.2016


Ответы (8)


Как отметили другие респонденты (@lukas и @mradzinski), Пикассо лишь слабо указывает на объект Target. Хотя вы можете хранить сильную ссылку Target в одном из ваших классов, это все равно может быть проблематично, если Target каким-либо образом ссылается на View, поскольку вы фактически также будете хранить сильную ссылку на этот View (что является одним из вещи, которых Пикассо явно помогает вам избежать).

Если вы находитесь в такой ситуации, я бы рекомендовал пометить Target на View:

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

Преимущество этого подхода в том, что Пикассо все сделает за вас. Он будет управлять объектами WeakReference для каждого из ваших представлений — как только один из них станет не нужен, любой Target, обрабатывающий изображение, также будет освобожден, так что вы не застряли с утечками памяти из-за долгоживущих целей, а ваша цель будет длиться до тех пор, пока жив его вид.

person wrb    schedule 13.11.2014
comment
ДА! Я решил создать параллельный список целей (List‹Target›), чтобы сохранить все целевые объекты, но ваше решение лучше. Цель будет существовать до тех пор, пока жив ее вид. Спасибо. - person psv; 15.11.2014
comment
Спас мой день. Спасибо. - person cy198706; 16.06.2015
comment
У меня нет просмотра изображения, как я могу решить эту проблему? Когда имеешь дело с такого рода ситуациями, gc — твой злейший враг. - person tim687; 11.08.2015
comment
Это решение элегантно. - person Youtoo; 21.09.2015
comment
Вы даже можете сохранить его в ArrayList‹Target›, и он будет работать, просто не забудьте очистить этот массив :-) - person Oliver Dixon; 22.02.2016
comment
В onBitmapLoaded и onBitmapFailed я также делаю imageView.setTag(null) после обработки растрового изображения. Разве это не нужно? - person Jaguar; 30.07.2016
comment
Благодарю вас! Просто спас мне жизнь :) - person yusufiga; 20.09.2016
comment
Разве мы не должны аннулировать тег в onBitmapLoaded/onBitmapFailed? - person WindRider; 26.05.2017
comment
Спасибо. Очень четкий ответ на небольшую, но важную деталь. - person Zafer; 15.01.2019
comment
Лучшее решение на данный момент! - person Shahood ul Hassan; 15.10.2019
comment
@ tim687 У меня тоже нет представления, поэтому мне не к чему пометить цель. Я знаю, что это старо, но вы нашли решение? - person Cody; 10.12.2019

Picasso не содержит строгой ссылки на объект Target, поэтому он удаляется сборщиком мусора и onBitmapLoaded не вызывается.

Решение довольно простое, просто сделайте сильную ссылку на файл 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);
   }
}      
person lukas    schedule 07.07.2014
comment
Или заставьте свой View реализовать Target. - person dnkoutso; 12.07.2014
comment
в документах говорится, что вы должны переопределить методы Object.equals(Object) и Object.hashCode(). у вас есть рабочий образец? - person chip; 21.07.2014
comment
где написано? У меня все еще есть проблема, даже если я сильно ссылаюсь на мой Target(). - person psv; 29.07.2014
comment
Я сейчас установил okHttp, он загружается немного быстрее, но у меня все еще та же проблема при первом запуске. Любые идеи ? - person psv; 29.07.2014
comment
@psv: Вам удалось решить проблему с первым запуском Picasso? У меня точно такая же проблема? Если вы решили, как вы решили это? - person TheDevMan; 16.09.2014
comment
@TheDevMan, нет, это не решено. Обходное решение, которое я использовал: сохраните каждый целевой объект в ArrayList, чтобы избежать сборки мусора. Это грязно и снижает производительность, но у меня нет другого решения. - person psv; 25.09.2014

Если бы у меня был ImageView, я бы просто сделал так: imageView.setTag(target);

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

Таким образом, создайте Set, ведь он будет хранить целевые объекты и удалять их по завершении загрузки.

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

        }
    }
person Flinbor    schedule 06.06.2015

Вот решение для тех, кто не использует представление. Этот вспомогательный метод использует список для временного хранения целевого объекта до тех пор, пока не будет возвращен результат, чтобы он не был удален:

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);
}
person DroidT    schedule 01.02.2018

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


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

с другой стороны -> нет обратного вызова, и вы не можете вызвать эту функцию в основном потоке, вы должны запустить эту функцию в фоновом потоке, как в следующем примере:


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

Еще одна вещь, которая работает намного лучше, — просто использовать Glide!

Мне нужно было использовать их оба, поскольку целью моего проекта было использование двух разных API для загрузки изображений, чтобы показать галерею изображений и дать пользователю возможность выбрать, какой API использовать.

Должен сказать, я был поражен результатами, API-интерфейс Glide работал безупречно во всех аспектах (цель Glide не имеет слабой ссылки), а Picasso устроил мне ад (это был мой первый раз, когда я использовал Glide, до сих пор я обычно использовал Picasso, похоже, сегодня все изменится ^^ ).

person Roee    schedule 22.07.2016

Как сказал (и цитировал) @lukas, Пикассо не имеет строгой ссылки на целевой объект. Чтобы избежать сборки мусора, вы должны иметь сильную ссылку на объект.

О методе fetch(). В документации довольно ясно, что fetch() не следует использовать ни с ImageView, ни с Target, это просто «разогрев» кеша и ничего больше, поэтому вы не сможете использовать его так, как вы хотеть.

Я рекомендую вам иметь сильную ссылку, как объяснил @lukas, она должна работать. Если нет, пожалуйста, откройте новый выпуск на странице проекта GitHub.

person mradzinski    schedule 21.08.2014

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

 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'
person khushbu    schedule 24.06.2020

person    schedule
comment
Это тоже решило мою проблему. я хотел использовать его с уведомлением. иногда изображение загружалось с помощью Target, а иногда нет. но после использования ImageView я смог загружать изображения каждый раз - person Raveesh G S; 31.03.2019
comment
в моем случае, кроме всего, это было лучшее решение! - person Noor Hossain; 25.03.2020