첫 번째로드시 호출되지 않은 대상 오브젝트의 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 ()가 호출되지 않습니다. fetch (Target t) 메소드를 사용하도록 명령하는 https://github.com/square/picasso/issues/39 와 같은 주제를 읽었습니다 (약한 참조의 문제 인 것 같습니다 ...) picasso (2.3.2)의 마지막 릴리스에서는 사용할 수 없습니다. 나는 fetch () 메소드 만 가지고 있지만 동시에 (mytarget)에 사용할 수는 없습니다

커스텀 Target으로 fetch ()를 사용하는 방법을 설명해 주시겠습니까? 감사합니다.

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


1
okhttp 2.0.0을 사용해야합니다. Okhttp 1.6.0에서 Picasso 2.3.2를 사용할 때도 동일한 문제가 발생합니다.
hakim

github.com/square/okhttp afaik, Picasso 2.3.2를 사용하여 okhttp (및 okio) 라이브러리를 포함하는 경우 필수입니다. 당신은 일식 또는 안드로이드 스튜디오를 사용하고 있습니까?
hakim

IntelliJ를 사용하고 있습니다. 나는 gradle 의존성을 보았고, okhttp를 보지 못했다 ... Picasso는 그것없이 작동하는 것처럼 보인다
psv

@psv 마커를 사용하여 아래 솔루션을 어떻게 구현 했습니까?
Mustafa Güven

답변:


247

다른 응답자 (@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);

이 접근 방식은 Picasso가 모든 것을 처리하도록하는 이점이 있습니다. WeakReference각 뷰에 대한 객체를 관리합니다. 더 이상 필요하지 않은 즉시 Target, 이미지 처리가 릴리스 될 때마다 대상이 오래 지속되어 메모리 누수가 발생하지 않지만 대상은 지속됩니다. 그 견해가 살아있는 한.


15
내 하루를 구했다. 감사.
cy198706

24
이미지보기가 없습니다.이 문제를 어떻게 해결할 수 있습니까? 이런 종류의 상황을 다룰 때, gc는 더 나쁜 적입니다.
tim687

3
ArrayList <Target>에 저장할 수도 있고 작동합니다. arraylist를 지우는 것을 잊지 마십시오 :-)
Oliver Dixon

2
onBitmapLoaded 및 onBitmapFailed에서 비트 맵을 처리 한 후 imageView.setTag (null)도 수행하고 있습니다. 필요하지 않습니까?
재규어

1
감사합니다! 방금 내 생명을 구했습니다 :)
yusufiga

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

2
또는 View구현하십시오 Target.
dnkoutso

문서에서 재정의 Object.equals(Object)Object.hashCode()메소드 가 필요하다고 말합니다 . 작업 샘플이 있습니까?

어디에 기록되어 있습니까? 내 Target ()을 강력하게 참조해도 여전히 문제가 있습니다.
psv

이제 okHttp를 설치했습니다.로드하는 것이 조금 더 빠르지 만 첫 번째 실행에서 여전히 동일한 문제가 있습니다. 어떤 아이디어?
psv

@psv : picasso 첫 출시 문제를 해결하셨습니까? 나도 같은 문제가있어? 해결했다면 어떻게 해결 했습니까?
TheDevMan

25

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

        }
    }

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를 사용한 후 매번 이미지를로드 할 수있었습니다
Raveesh GS

1
내 경우에는 모두를 제외하고 이것이 가장 좋은 해결책이었습니다!
누르 호사 인

4

뷰를 사용하지 않는 사람들을위한 솔루션은 다음과 같습니다. 이 헬퍼 메소드는리스트를 사용하여 결과가 리턴 될 때까지 대상 오브젝트를 임시로 저장하여 gc되지 않습니다.

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

3

@lukas가 말하고 인용 한 것처럼 Picasso는 Target 객체에 대한 강력한 참조를 보유하지 않습니다. 가비지 콜렉션을 피하려면 오브젝트에 대한 강력한 참조를 보유해야합니다.

fetch () 메소드에 대해 문서에서 fetch ()가 ImageView 또는 Target과 함께 사용되지 않아야하며 캐시와 다른 것을 "따뜻하게"하는 것이므로 명확하지 않으므로 원하는 방식으로 사용할 수 없습니다. 필요.

@lukas와 같이 강력한 참조를 보유하는 것이 좋습니다. 그렇지 않으면 프로젝트의 GitHub 페이지에서 새로운 이슈를여십시오.


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

훨씬 더 잘 작동하는 또 다른 것은 글라이드를 사용하는 것입니다 !

내 프로젝트의 목적은 2 개의 다른 이미지 다운로드 API를 사용하여 이미지 갤러리를 표시하고 사용자에게 사용할 API를 선택할 수있는 기능을 제공했기 때문에 두 가지를 모두 사용해야했습니다.

나는 결과에 놀랐다. 글라이드의 API는 모든 측면에서 완벽하게 작동했다. (글라이드의 목표는 약한 참조가 없다) 피카소는 나에게 지옥을 주었다. 오늘처럼 보일 것 같습니다 ^^).


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'
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.