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

126

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

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 (), но я не могу использовать его в (mytarget) одновременно

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

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

PSV
источник
1
убедитесь , что использовать okhttp 2.0.0, я сталкиваюсь такая же проблема при использовании Picasso 2.3.2 с Okhttp 1.6.0
Hakim
github.com/square/okhttp afaik, если вы используете Picasso 2.3.2, обязательно включать библиотеку okhttp (и okio). вы используете eclipse или android studio?
хаким
Я использую IntelliJ. Я видел свои зависимости gradle, я не видел okhttp ... Кажется, Пикассо работает без него
psv
@psv как вы реализовали приведенное ниже решение с маркерами?
Мустафа Гювен,

Ответы:

248

Как отметили другие респонденты (@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обработка изображения также будет выпущена, поэтому вы не застрянете с утечками памяти из-за долгоживущих целей, но ваша цель будет длиться пока жив его взгляд.

WRB
источник
15
Спас мой день. Спасибо.
cy198706
24
У меня нет изображения, как мне тогда решить эту проблему? Когда вы имеете дело с подобными ситуациями, gc - ваш худший враг
tim687
3
Вы даже можете сохранить его в ArrayList <Target>, и он будет работать, просто не забудьте очистить этот массив :-)
Оливер Диксон,
2
В onBitmapLoaded и onBitmapFailed я также делаю imageView.setTag (null) после обработки растрового изображения. Разве это не нужно?
Jaguar
1
Спасибо! Просто спас мне жизнь :)
юсуфига
55

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);
   }
}      
Lukas
источник
2
Или сделайте свое Viewорудие Target.
dnkoutso
в документации, он говорит , что вы должны переопределить Object.equals(Object)и Object.hashCode()методы. у вас есть рабочий образец?
чип
где это написано? У меня все еще есть проблема, даже если я сильно ссылаюсь на мою Target ().
psv
Я сейчас установил okHttp, он загружается немного быстрее, но при первом запуске у меня все та же проблема. Любые идеи ?
psv
@psv: Удалось ли вам решить проблему с первым запуском Picasso? У меня та же проблема? Если вы решили, как вы это решили?
TheDevMan
25

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

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

Итак, команда create 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) {

        }
    }
Flinbor
источник
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() {

            }
        });
Рагхав Сатьядев
источник
1
Это тоже решило мою проблему. Я хотел использовать его с уведомлением. иногда изображение загружалось с помощью Target, а иногда нет. но после использования ImageView я мог загружать изображения каждый раз
Равиш Г.С.
1
в моем случае, кроме всего прочего, это было лучшее решение!
Нур Хоссейн,
4

Вот решение для тех, кто не использует представление. Этот вспомогательный метод использует список для временного хранения целевого объекта до тех пор, пока не будет возвращен результат, чтобы он не был 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
источник
3

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

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

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

mradzinski
источник
3

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


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 не имеет слабых ссылок), а Пикассо устроил мне ад (это был мой первый раз, когда я использовал Glide, до сих пор я обычно использовал Пикассо, похоже, что сегодня это изменится ^^).

Roee
источник
0

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

 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
источник