【问题标题】:Picasso image downloading & caching behaviorPicasso 图像下载和缓存行为
【发布时间】:2016-03-11 03:29:02
【问题描述】:

所以我最近想试试 Picasso 库的缓存功能,结果陷入了这种令人困惑的境地:

我从我的网络服务器(使用 Retrofit2)检索图像的文件 names 和 paths,并将它们存储到 ImageComponent 对象(模型)中:

public class ImageComponent {

    private int id; // 'id' in database
    private String filename; // image name
    private String path; // image path in server storage
    private Bitmap bitmap;

    // Overloaded constructor

    // Getters & setters

}

现在加载成功,我使用 Picasso 用这些图像填​​充RecyclerView。加载和膨胀过程是成功的,但是在缓存图像时会有点棘手。

案例1:使用android.util.LruCache

(为了方便,我把Recyclerview的适配器的全部代码贴出来,尽量简洁)

// imports
import android.util.LruCache;

public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> {

    private Context mContext; // Activity's context
    private List<ImageComponent> mImages; // The imageComponents to display

    // The contreversial, infamous cache
    private LruCache<Integer, Bitmap> mImageCache; 

    public ImageAdapter(Context context, List<ImageComponent> images) {
        mContext = context;
        mImages = images;

        // Provide 1/8 of available memory to the cache
        final int maxMemory = (int)(Runtime.getRuntime().maxMemory() /1024);
        final int cacheSize = maxMemory / 8;
        mImageCache = new LruCache<>(cacheSize);
    }

    @Override
    public ImageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // Nothing special
    }

    @Override
    public void onBindViewHolder(final ImageAdapter.ViewHolder holder, final int position) {
        // Current ImageComponent
        ImageComponent imageComponent = mImages.get(position);

        // Full image path in server storage
        String imagePath = Constants.SERVER_IP_ADDRESS + Constants.UPLOADS_DIRECTORY
                + imageComponent.getPath();

        // Display the file's name
        holder.text.setText(imageComponent.getFilename());

        final ImageView imageView = holder.image;

        // Get bitmap from cache, check if it exists or not
        Bitmap bitmap = mImageCache.get(imageComponent.getId());
        if (bitmap != null) {
            Log.i("ADAPTER", "BITMAP IS NOT NULL - ID = " + imageComponent.getId());
            // Image does exist in cache
            holder.image.setImageBitmap(imageComponent.getBitmap());
        }
        else {
            Log.i("ADAPTER", "BITMAP IS NULL");

            // Callback to retrieve image, cache it & display it
            final Target target = new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    ImageComponent img = mImages.get(position);

                    // Display image
                    holder.image.setImageBitmap(bitmap);

                    // Cache the image
                    img.setBitmap(bitmap);
                    mImages.set(position, img);
                    mImageCache.put(img.getId(), bitmap);
                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                }
            };

            // Tag the target to the view, to keep a strong reference to it
            imageView.setTag(target);
            // Magic
            Picasso.with(mContext)
                    .load(imagePath)
                    .into(target);
        }
    }

    @Override
    public int getItemCount() {
        return mImages.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        ImageView image;
        TextView text;

        // Constructor & view binding, not that special
    }
}

结果1

(注意最后两张图片,以及在显示正确图片之前它们如何显示其他之前的图片)

几点说明:

  • 我遇到了一个问题,图像根本没有显示。经过一番研究,我发现this answer 建议绑定 target 到ImageView。 (工作)
  • 我不太明白毕加索是如何缓存图像的。是自动过程还是手动过程? This answer 表示毕加索会为您处理这项任务。但是当我实际尝试时(没有 android Lrucache),似乎没有完成缓存:每次我来回滚动时,图像都会重新加载。
  • 实际上,我打算使用 Picasso 的 Lrucache 发布 第二个 用例,该用例出现了更严重的问题(图片是随机显示的,&每次滚动都会改变),但我认为这篇文章已经足够长了。

我的问题是:

  1. 为什么我会出现这种奇怪的行为?(如附件 GIF 所示)
  2. 整个缓存过程是如何工作的?在使用 Picasso 时我应该(或者我可以)使用 Lrucache 吗?
  3. SDK 自带的Lrucache 和Picasso 有什么区别? (性能、最佳用例场景等...)

【问题讨论】:

  • 缓存可能与您的图像问题无关。您的 ImageView 被重用,Target 将图像放入其中,然后稍后的 Target 完成并将更新的图像放入其中。
  • @NightlyNexus 我开始也是这么想的,但是我的适配器代码中看不到任何错误...
  • 您应该覆盖 Target 上的 equals 和 hashCode。否则,毕加索不知道取消之前的请求。 square.github.io/picasso/2.x/picasso/com/squareup/picasso/…
  • 至于缓存,是的,Picasso 默认使用 LruCache 实现。
  • 我试试看,谢谢你的回复!

标签: java android caching picasso android-lru-cache


【解决方案1】:
  1. 我认为同时使用 LRU 缓存和 Picasso 会导致奇怪的行为。我使用毕加索将图像缓存到适配器,它工作得很好。你可以签到here

  2. Picasso 与适配器一起使用时会自动缓存图像,它会像这样缓存,如果列表/回收器视图的子项不可见,它将停止为相应的子项缓存图像。所以最好使用 Picasso单独使用适配器。

  3. Picasso 优于 LRU 缓存的主要用途是,Picasso 易于使用。 ex : 在 Picasso 中指定内存缓存大小。

    Picasso picasso = new Picasso.Builder(context)
                              .memoryCache(new LruCache(250))
                              .build();
    

Picasso 还允许您在下载出现错误时使用图像通知用户,这是在加载完整图像之前 Imageview 的默认持有者。

希望对您有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-02
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 2021-04-30
    • 2016-06-25
    • 2016-09-03
    • 1970-01-01
    相关资源
    最近更新 更多