【问题标题】:onBitmapLoaded of Target object not called on first load首次加载时未调用目标对象的 onBitmapLoaded
【发布时间】:2014-06-12 09:22:27
【问题描述】:

在我的功能中:

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)中不可用。 2)。我只有一个 fetch() 方法,但我不能同时使用 into(mytarget)

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

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

【问题讨论】:

  • 确保使用 okhttp 2.0.0,我在使用 Picasso 2.3.2 和 Okhttp 1.6.0 时遇到同样的问题
  • github.com/square/okhttp afaik,如果您使用 Picasso 2.3.2 来包含 okhttp(和 okio)库,这是强制性的。你用的是eclipse还是android studio?
  • 我正在使用 IntelliJ。我已经看到了我的 gradle 依赖项,我没有看到 okhttp...Picasso 似乎没有它也可以工作
  • @psv 您是如何使用标记实现以下解决方案的?

标签: picasso


【解决方案1】:

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

如果您遇到这种情况,我建议您将Target 标记为View

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

这种方法的好处是让毕加索为您处理一切。它将为您的每个视图管理 WeakReference 对象 - 一旦不再需要,无论 Target 处理图像也将被释放,因此您不会因长期目标而陷入内存泄漏,但只要它的视图还活着,你的 Target 就会一直存在。

【讨论】:

  • 拯救了我的一天。谢谢。
  • 我没有图像视图,那我该如何解决这个问题呢?在处理这种情况时,gc 是你最大的敌人
  • 你甚至可以将它存储在一个 ArrayList 中,它会起作用,只要记住清除那个 arraylist :-)
  • 在onBitmapLoaded和onBitmapFailed中,处理完位图后我也在做imageView.setTag(null)。不需要吗?
  • 谢谢!刚刚救了我的命:)
【解决方案2】:

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

【讨论】:

  • 或者让你的View实现Target
  • 在文档中,它说您必须覆盖 Object.equals(Object)Object.hashCode() 方法。你有工作样本吗?
  • 写在哪里?即使强烈引用我的 Target(),我仍然有我的问题。
  • 我现在已经安装了okHttp,加载速度有点快,但我第一次启动时仍然遇到同样的问题。有什么想法吗?
  • @psv:你解决了毕加索首次发射的问题吗?我也有同样的问题?如果你解决了,你是怎么解决的?
【解决方案3】:

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

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

因此 create Set 女巫将存储 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) {

        }
    }

【讨论】:

    【解决方案4】:
    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() {
    
                }
            });
    

    【讨论】:

    • 它也解决了我的问题。我想将它与通知一起使用。有时图像是使用 Target 下载的,有时不是。但是在使用 ImageView 之后,我每次都能加载图像
    • 就我而言,除此之外,这是最好的解决方案!
    【解决方案5】:

    我遇到了类似的问题,并且持有对目标的引用根本没有帮助,所以我使用了以下返回位图的代码:


    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到目前为止,似乎今天它会改变^^)。

    【讨论】:

      【解决方案6】:

      这里是那些不使用视图的人的解决方案。这个辅助方法使用一个列表来临时存储目标对象,直到返回一个结果,这样它就不会被 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);
      }
      

      【讨论】:

        【解决方案7】:

        就像@lukas 所说(和引用),毕加索没有对目标对象的强引用。为避免垃圾回收,您必须持有对该对象的强引用。

        关于 fetch() 方法。在文档中很清楚 fetch() 不能与 ImageView 或 Target 一起使用,它只是为了“预热”缓存而不是其他任何东西,所以你不能像你一样使用它想要。

        我建议你像@lukas 解释的那样持有一个强有力的参考,它应该可以工作。如果没有,请在项目的 GitHub 页面上打开一个新问题。

        【讨论】:

          【解决方案8】:

          我也遇到过同样的问题,但是当我如下所述更改依赖项时,它现在可以正常工作了。

           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'
          

          【讨论】:

            猜你喜欢
            • 2016-02-08
            • 1970-01-01
            • 2016-02-09
            • 1970-01-01
            • 1970-01-01
            • 2014-11-16
            • 2018-09-02
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多