[picasso] 첫 번째로드시 호출되지 않은 대상 오브젝트의 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 ()가 호출되지 않습니다. 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–



답변

다른 응답자 (@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, 이미지 처리가 릴리스 될 때마다 대상이 오래 지속되어 메모리 누수가 발생하지 않지만 대상은 지속됩니다. 그 견해가 살아있는 한.


답변

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


답변

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

        }
    }


답변

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

            }
        });


답변

뷰를 사용하지 않는 사람들을위한 솔루션은 다음과 같습니다. 이 헬퍼 메소드는리스트를 사용하여 결과가 리턴 될 때까지 대상 오브젝트를 임시로 저장하여 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);
}


답변

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

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

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


답변

비슷한 문제가 발생하여 대상에 대한 참조가 전혀 도움이되지 않았으므로 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는 모든 측면에서 완벽하게 작동했다. (글라이드의 목표는 약한 참조가 없다) 피카소는 나에게 지옥을 주었다. 오늘처럼 보일 것 같습니다 ^^).