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

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

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

7 ответов

Решение

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

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

Решение очень простое, просто сделайте сильную ссылку на цель.

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

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

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

Поэтому создание Set witch сохранит целевые объекты и удалит их по окончании загрузки.

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

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

            }
        });

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


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!

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

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

Вот решение для тех, кто не использует представление. Этот вспомогательный метод использует список для временного хранения целевого объекта до тех пор, пока не будет возвращен результат, чтобы он не был 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(Drawable errorDrawable) {
            targets.clear();
            callback.onFailure(null);
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
        }
    };
    targets.add(target);
    Picasso.with(context).load(url).into(target);
}

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

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

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

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

 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'
Другие вопросы по тегам