毕加索图像下载&缓存行为

时间:2015-12-06 13:51:24

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

所以我最近想尝试Picasso库的缓存功能,&我遇到了这种令人困惑的情况:

我检索图像'档案名称 s&来自我的网络服务器的路径(使用Retrofit2),&我将它们存储到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
    }
}

RESULT1

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

Result1

一些注意事项:

  • 我遇到了一个问题,根本没有显示图像。经过一些研究,我发现this answer建议绑定 targetImageView。 (工作)
  • 我不太了解Picasso如何缓存图像。它是自动还是手动过程? This answer指出毕加索为你处理这项任务。但是当我实际尝试它(没有Android Lrucache)时,似乎没有缓存:每次我向后滚动时都会重新加载图像。类推。
  • 实际上我会发布一个第二个用例,其中使用 Picasso Lrucache更加错误了(图片是随机显示,并随着每个滚动而改变),但我认为这篇文章已经足够长了。

我的问题是:

  1. 为什么我会得到那种奇怪的行为?(如附件 GIF 所示)
  2. 整个缓存过程如何工作?在使用 Picasso 时我应该(或者我可以)使用Lrucache吗?
  3. SDK附带的Lrucache之间的区别&amp; 毕加索&#39; (性能,最佳用例场景等......)

1 个答案:

答案 0 :(得分:0)

  1. 我认为使用LRU缓存和Picasso会导致奇怪的行为。我使用Picasso将Image缓存到适配器,它完全正常。您可以登入here

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

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

    Picasso picasso = new Picasso.Builder(context)
                              .memoryCache(new LruCache(250))
                              .build();
    
  4. Picasso还允许您在下载时出现图像时通知用户,在加载完整图像之前是Imageview的默认持有者。

    希望它有所帮助。