【发布时间】: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发布 第二个 用例,该用例出现了更严重的问题(图片是随机显示的,&每次滚动都会改变),但我认为这篇文章已经足够长了。
我的问题是:
- 为什么我会出现这种奇怪的行为?(如附件 GIF 所示)
- 整个缓存过程是如何工作的?在使用 Picasso 时我应该(或者我可以)使用 Lrucache 吗?
- 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