首次加载时未调用Target对象的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)方法(这似乎是参考力弱的问题...),但是该功能在最新版本的毕加索(2.3.2)中不可用。我只有一个fetch()方法,但不能同时使用into(mytarget)

您能否解释一下如何将fetch()与自定义目标一起使用?谢谢。

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


1
确保使用okhttp 2.0.0,将Picasso 2.3.2与Okhttp 1.6.0一起使用时会遇到相同的问题
hakim 2014年

github.com/square/okhttp afaik,如果您要使用Picasso 2.3.2包含okhttp(和okio)库,则必须这样做。您正在使用eclipse还是android studio?
哈基姆2014年

我正在使用IntelliJ。我已经看到了我的gradle这个依存关系,我没有看到okhttp ......毕加索似乎工作,而它
埃因霍温

@psv如何使用标记实现以下解决方案?
MustafaGüven16年

Answers:


247

正如其他受访者(@lukas和@mradzinski)所指出的那样,毕加索只保留了对该Target对象的弱引用。虽然您可以Target在一个类中存储一个强引用,但是如果以任何方式Target引用它View,仍然会出现问题,因为您也将有效地保持对该类的强引用View(这是毕加索的事情之一)明确地帮助您避免)。

如果您遇到这种情况,建议您将标记TargetView

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

这种方法的好处是让毕加索可以为您处理所有事情。它将WeakReference为您的每个视图管理对象-不再需要Target该视图时,将释放对图像的任何处理,因此您不会因为目标寿命长而陷入内存泄漏,但是目标将持久只要它的观点还活着。


15
拯救了我的一天。谢谢。
cy198706

24
我没有图像视图,那我该如何解决这个问题?当这种情况处理时,GC是你的敌人更坏
tim687

3
您甚至可以将其存储在ArrayList <Target>中,它将起作用,只记得清除该arraylist :-)
Oliver Dixon

2
在onBitmapLoaded和onBitmapFailed中,在处理位图后,我也在做imageView.setTag(null)。不需要吗?
美洲虎

1
谢谢!刚刚救了我的命:)
yusufiga

55

毕加索(Picasso)对目标对象没有强大的引用,因此它正在被垃圾回收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 2014年

在文档中,它说您必须重写Object.equals(Object)Object.hashCode()方法。你有工作样品吗?
筹码

它写在哪里?即使对我的Target()进行了强烈引用,我仍然遇到问题。
psv 2014年

我现在已经安装了okHttp,它的加载速度更快,但是在第一次启动时仍然存在相同的问题。有任何想法吗 ?
psv 2014年

@psv:您是否解决了毕加索的首次发布问题?我也有同样的问题?如果您解决了,您是如何解决的?
TheDevMan 2014年

25

如果我有ImageView,我将简单地像这样:imageView.setTag(target);

我使用下一个解决方案将位图加载到通知中,因此我只需要位图。

因此,create Set witch将存储Target对象,并在完成加载后将其删除。

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
它也解决了我的问题。我想将其与Notification一起使用。有时映像是使用Target下载的,有时不是。但是使用ImageView之后,我每次都能加载图像
Raveesh GS

1
就我而言,这是最好的解决方案!
Noor Hossain

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

3

就像@lukas所说(和引用)一样,毕加索对目标对象也没有很强的引用。为了避免垃圾回收,您必须对对象拥有强大的引用。

关于fetch()方法。在文档中很明显,fetch()不能与ImageView或Target一起使用,而只是“预热”缓存,别无其他,因此您将无法以自己的方式使用它想。

我建议您持有像@lukas这样的强引用,它应该可以工作。如果没有,请在该项目的GitHub页面上打开一个新期刊。


3

我遇到了类似的问题,对目标的引用完全没有帮助,所以我使用了下面的代码返回位图:


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。

我必须说,我对结果感到惊讶,Glide的api在各个方面都完美无缺(Glide的目标没有弱引用),而Picasso却给了我一个地狱(那是我第一次使用Glide,到目前为止我通常都使用Picasso,好像今天会改变^^)。


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'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.