onBitmapLoaded obiektu docelowego nie wywołano przy pierwszym załadowaniu

126

W mojej funkcji:

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

Metoda onBitmapLoaded () nigdy nie jest wywoływana przy pierwszym ładowaniu zdjęć. Przeczytałem jakiś temat, taki jak https://github.com/square/picasso/issues/39, który zaleca użycie metody pobierania (Target t) (wydaje się, że jest to problem ze słabym odniesieniem ...), ale ta funkcja nie jest dostępny w ostatnim wydaniu Picassa (2.3.2). Mam tylko metodę fetch (), ale nie mogę używać metody into (mytarget) w tym samym czasie

Czy możesz mi wyjaśnić, jak używać funkcji fetch () z niestandardowym celem? Dziękuję Ci.

Doc: http://square.github.io/picasso/javadoc/com/squareup/picasso/RequestCreator.html#fetch--

psv
źródło
1
Należy używać okhttp 2.0.0, I napotkać ten sam problem przy korzystaniu z Okhttp Picasso 2.3.2 1.6.0
Hakim
github.com/square/okhttp afaik , jest to obowiązkowe, jeśli używasz Picasso 2.3.2, aby dołączyć bibliotekę okhttp (i okio). czy używasz Eclipse czy Android Studio?
hakim
Używam IntelliJ. Widziałem zależności od gradle, nie widziałem okhttp ... Picasso wydaje się działać bez tego
psv
@psv Jak zaimplementowałeś poniższe rozwiązanie ze znacznikami?
Mustafa Güven

Odpowiedzi:

247

Jak zauważyli inni respondenci (@lukas i @mradzinski), Picasso zachowuje jedynie słabe odniesienie do Targetobiektu. Chociaż możesz przechowywać silne odniesienie Targetw jednej ze swoich klas, nadal może to być problematyczne, jeśli Targetodniesienia a Vieww jakikolwiek sposób, ponieważ skutecznie zachowasz również silne odniesienie do tego View(co jest jedną z rzeczy, które Picasso wyraźnie pomaga uniknąć).

Jeśli jesteś w takiej sytuacji, polecam otagowanie Targetjako View:

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

Takie podejście ma tę zaletę, że Picasso zajmie się wszystkim za Ciebie. Będzie zarządzać WeakReferenceobiektami dla każdego z twoich widoków - gdy tylko jeden nie będzie już potrzebny, wszelkie Targetprzetwarzanie obrazu również zostanie zwolnione, więc nie utkniesz z wyciekami pamięci z powodu długowiecznych celów, ale Twój Cel przetrwa dopóki jego widok jest żywy.

wrb
źródło
15
Uratowałem mój dzień. Dzięki.
cy198706
24
Nie mam widoku obrazu, jak mogę rozwiązać ten problem? Kiedy
radzisz
3
Możesz nawet przechowywać go w ArrayList <Target> i będzie działać, pamiętaj tylko, aby wyczyścić tę arraylistę :-)
Oliver Dixon
2
W onBitmapLoaded i onBitmapFailed robię również imageView.setTag (null) po przetworzeniu mapy bitowej. Czy to nie jest potrzebne?
Jaguar
1
Dziękuję Ci! Właśnie uratowałem mi życie :)
yusufiga
55

Picasso nie ma silnego odniesienia do obiektu docelowego, dlatego jest on zbierany jako śmieci i onBitmapLoadednie jest wywoływany.

Rozwiązanie jest dość proste, wystarczy odnieść się do pliku 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
źródło
2
Lub zrób swoje Viewnarzędzie Target.
dnkoutso,
w dokumentacji mówi, że musisz zastąpić Object.equals(Object)i Object.hashCode()metody. czy masz działającą próbkę?
chip
gdzie jest napisane? Nadal mam swój problem, nawet gdy robię silne odwołanie do mojego elementu Target ().
psv
Mam teraz zainstalowany okHttp, ładuje się trochę szybciej, ale nadal mam ten sam problem przy pierwszym uruchomieniu. Jakieś pomysły ?
psv
@psv: Czy udało Ci się rozwiązać problem z pierwszym uruchomieniem Picassa? Mam ten sam problem? Jeśli rozwiązałeś, jak to rozwiązałeś?
TheDevMan
25

Gdybym miał ImageView, zrobiłbym tak: imageView.setTag (target);

Używam kolejnego rozwiązania do ładowania map bitowych do powiadomień, więc potrzebuję tylko bitmapy.

Stwórz więc zestaw, który będzie przechowywać obiekty docelowe i usuwać je po zakończeniu ładowania.

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
źródło
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() {

            }
        });
Raghav Satyadev
źródło
1
To też rozwiązało mój problem. chciałem go używać z powiadomieniem. czasami obraz był pobierany przez Target, a czasami nie. ale po użyciu ImageView mogłem ładować obrazy za każdym razem
Raveesh GS
1
w moim przypadku oprócz wszystkich to było najlepsze rozwiązanie!
Noor Hossain
4

Oto rozwiązanie dla tych, którzy nie używają widoku. Ta metoda pomocnicza używa listy do tymczasowego przechowywania obiektu docelowego, dopóki wynik nie zostanie zwrócony, aby nie był 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
źródło
3

Jak powiedział @lukas (i cytując), Picasso nie ma silnego odniesienia do obiektu docelowego. Aby uniknąć wyrzucania elementów bezużytecznych, musisz mieć silne odwołanie do obiektu.

Informacje o metodzie fetch (). W dokumentacji jest całkiem jasne, że funkcja fetch () nie może być używana z ImageView ani Targetem, służy ona tylko do "rozgrzania" pamięci podręcznej i nic więcej, więc nie będziesz mógł jej używać w taki sposób chcieć.

Zalecam trzymanie silnego odniesienia, takiego jak wyjaśnił @lukas, powinno działać. Jeśli nie, otwórz nowy numer na stronie GitHub projektu.

mradzinski
źródło
3

Napotkałem podobny problem i trzymanie odniesienia do celu w ogóle nie pomogło, więc użyłem następującego kodu, który zwraca Bitmapę:


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

z drugiej strony -> nie ma wywołania zwrotnego i nie możesz wywołać tej funkcji w głównym wątku, musisz uruchomić tę funkcję w wątku w tle, jak w poniższym przykładzie:


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

Kolejną rzeczą, która działa o wiele lepiej, jest użycie Glide!

Musiałem użyć obu z nich, ponieważ celem mojego projektu było użycie 2 różnych interfejsów API do pobierania obrazów, aby wyświetlić galerię obrazów i dać użytkownikowi możliwość wyboru interfejsu API.

Muszę powiedzieć, że byłem zdumiony wynikami, api Glide'a działało bez zarzutu pod każdym względem (cel Glide'a nie ma słabych odniesień) wile Picasso dał mi piekło (to był mój pierwszy raz z Glide, zwykle używałem Picassa do tej pory, wygląda na to, że dzisiaj to się zmieni ^^).

Roee
źródło
0

Miałem ten sam problem, ale kiedy zmienię zależność, jak wspomniano poniżej, teraz działa poprawnie.

 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
źródło