我有一個使用LruCache類的全局位圖緩存。當爲列表視圖加載縮略圖時,首先使用緩存。它工作正常。Android:使用位圖的LruCache問題
但有一個問題是:有時從緩存中的位圖實例無法顯示在列表視圖上。似乎來自緩存的這種位圖不再有效。我已經檢查了緩存中的位圖,如果它不是空的,並且它沒有被回收,但它仍然看起來像這樣的位圖不能被顯示(即使它不是空的,也不會被回收)。
緩存類:
public class ImageCache {
private LruCache<String, Bitmap> mMemoryCache;
private static ImageCache instance;
public static ImageCache getInstance() {
if(instance != null) {
return instance;
}
instance = new ImageCache();
instance.initializeCache();
return instance;
}
protected void initializeCache() {
final int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory/8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount()/1024;
}
};
}
public Bitmap getImage(String url) {
return this.mMemoryCache.get(url);
}
public void cacheImage(String url, Bitmap image) {
this.mMemoryCache.put(url, image);
}
}
,並使用高速緩存中的代碼是在適配器類,這是的CursorAdapter的亞類:
final ImageCache cache = ImageCache.getInstance();
// First get from memory cache
final Bitmap bitmap = cache.getImage(thumbnailUrl);
if (bitmap != null && !bitmap.isRecycled()) {
Log.d(TAG, "The bitmap is valid");
viewHolder.imageView.setImageBitmap(bitmap);
}
else {
Log.d(TAG, "The bitmap is invalid, reload it.");
viewHolder.imageView.setImageResource(R.drawable.thumbnail_small);
// use the AsyncTask to download the image and set in cache
new DownloadImageTask(context, viewHolder.imageView, thumbnailUrl, dir, filepath).execute();
}
DownloadImageTask的代碼:
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
private ImageView mImageView;
private String url;
private String dir;
private String filename;
private Context context;
public DownloadImageTask(Context context, ImageView imageView, String url, String dir, String filename) {
this.mImageView = imageView;
this.url = url;
this.filename = filename;
this.dir = dir;
this.context = context;
this.cache = cache;
}
protected Bitmap doInBackground(String... urls) {
// String urldisplay = urls[0];
final Bitmap bitmap = FileUtils.readImage(context, dir, filename, url);
return bitmap;
}
protected void onPostExecute(Bitmap result) {
final ImageCache cache = ImageCache.getInstance();
if(result != null) {
cache.put(url, result);
mImageView.setImageBitmap(result);
}
}
}
任何幫助將不勝感激。謝謝!
更新:我遵循greywolf82建議的link:「處理配置更改」部分。我將以下屬性放入我的活動類和兩個片段類中:
public LruCache mMemoryCache;
在活動課,我嘗試初始化調用片段時,高速緩存:
// Get the cache
mMemoryCache = mIndexFragment.mRetainedCache;
if (mMemoryCache == null) {
final int maxMemory = (int) (Runtime.getRuntime().maxMemory()/1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory/8;
// Initialize the cache
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount()/1024;
}
};
Log.d(TAG, "Initialized the memory cache");
mIndexFragment.mRetainedCache = mMemoryCache;
}
片段中的類: setRetainInstance(真);
我將緩存實例傳遞給適配器構造函數,以便適配器可以使用緩存。
但我仍然有同樣的問題。
更新2:
兩個適配器類有改動接受LruCache實例:
NewsCursorAdapter:
public class NewsCursorAdapter extends CursorAdapter {
private static final String TAG = "NewsCursorAdapter";
private LruCache<String, Bitmap> cache;
private Context mContext;
public NewsCursorAdapter(Context context, LruCache<String, Bitmap> cache) {
super(context, null, false);
this.mContext = context;
this.cache = cache;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
final Setting setting = ApplicationContext.getSetting();
// Get the view holder
ViewHolder viewHolder = (ViewHolder) view.getTag();
final String thumbnail = cursor.getString(NewsContract.Entry.THUMBNAIL_CURSOR_INDEX);
if(thumbnail != null) {
String pictureDate = cursor.getString(NewsContract.Entry.PIC_DATE_CURSOR_INDEX);
final String dir = "thumbnails/" + pictureDate + "/";
final String filepath = thumbnail + "-small.jpg";
final String thumbnailUrl = setting.getCdnUrl() + dir + filepath;
//final ImageCache cache = ImageCache.getInstance();
// First get from memory cache
final Bitmap bitmap = cache.get(thumbnailUrl);
if (bitmap != null && !bitmap.isRecycled()) {
Log.d(TAG, "The bitmap is valid: " + bitmap.getWidth());
viewHolder.imageView.setImageBitmap(bitmap);
}
else {
Log.d(TAG, "The bitmap is invalid, reload it.");
viewHolder.imageView.setImageResource(R.drawable.thumbnail_small);
new DownloadImageTask(viewHolder.imageView, thumbnailUrl, dir, filepath).execute();
}
}
else {
viewHolder.imageView.setVisibility(View.GONE);
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.listview_item_row, parent,
false);
// Initialize the view holder
ViewHolder viewHolder = new ViewHolder();
viewHolder.titleView = (TextView) view.findViewById(R.id.title);
viewHolder.timeView = (TextView) view.findViewById(R.id.news_time);
viewHolder.propsView = (TextView) view.findViewById(R.id.properties);
viewHolder.imageView = (ImageView) view.findViewById(R.id.icon);
view.setTag(viewHolder);
return view;
}
static class ViewHolder {
TextView titleView;
TextView timeView;
TextView propsView;
ImageView imageView;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
private ImageView mImageView;
private String url;
private String dir;
private String filename;
public DownloadImageTask(ImageView imageView, String url, String dir, String filename) {
this.mImageView = imageView;
this.url = url;
this.filename = filename;
this.dir = dir;
}
protected Bitmap doInBackground(String... urls) {
final Bitmap bitmap = FileUtils.readImage(mContext, dir, filename, url);
return bitmap;
}
protected void onPostExecute(Bitmap result) {
//final ImageCache cache = ImageCache.getInstance();
if(result != null) {
cache.put(url, result);
mImageView.setImageBitmap(result);
}
}
}
}
名單適配器,NewsTopicItemAdapter:
public class NewsTopicItemAdapter extends ArrayAdapter<NewsTopicItem> {
private Context context = null;
private EntryViewHolder viewHolder;
private HeaderViewHolder headerViewHolder;
private LruCache<String, Bitmap> mCache;
public NewsTopicItemAdapter(Context context, List<NewsTopicItem> arrayList, LruCache<String, Bitmap> cache) {
super(context, 0, arrayList);
this.context = context;
this.mCache = cache;
}
public void setItems(List<NewsTopicItem> items) {
this.addAll(items);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final NewsTopicItem item = getItem(position);
View view;
if(!item.isHeader()) {
view = this.getEntryView((NewsTopicEntry)item, convertView, parent);
}
else {
view = this.getHeaderView((NewsTopicHeader)item, convertView, parent);
}
return view;
}
protected View getEntryView(NewsTopicEntry newsItem, View convertView, ViewGroup parent) {
View view;
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
viewHolder = new EntryViewHolder();
view = inflater.inflate(R.layout.listview_item_row, parent,
false);
// Initialize the view holder
viewHolder.titleView = (TextView) view.findViewById(R.id.title);
viewHolder.timeView = (TextView) view.findViewById(R.id.news_time);
viewHolder.propsView = (TextView) view.findViewById(R.id.properties);
viewHolder.imageView = (ImageView) view.findViewById(R.id.icon);
view.setTag(viewHolder);
viewHolder.propsView.setText(newsItem.getSource());
if (newsItem.getThumbnail() != null) {
final String dir = "thumbnails/" + newsItem.getPictureDate() + "/";
final String filepath = newsItem.getThumbnail() + "-small.jpg";
final String thumbnailUrl = "http://www.oneplusnews.com/static/" + dir + filepath;
//final ImageCache cache = ImageCache.getInstance();
// First get from memory cache
final Bitmap bitmap = mCache.get(thumbnailUrl);
if (bitmap != null && !bitmap.isRecycled()) {
viewHolder.imageView.setImageBitmap(bitmap);
} else {
viewHolder.imageView.setImageResource(R.drawable.thumbnail_small);
new DownloadImageTask(viewHolder.imageView, thumbnailUrl, dir, filepath).execute();
}
}
else {
viewHolder.imageView.setVisibility(View.GONE);
}
viewHolder.titleView.setText(newsItem.getTitle());
viewHolder.timeView.setText(DateUtils.getDisplayDate(newsItem.getCreated()));
return view;
}
protected View getHeaderView(NewsTopicHeader header, View convertView, ViewGroup parent) {
View view;
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
headerViewHolder = new HeaderViewHolder();
view = inflater.inflate(R.layout.news_list_header, parent,
false);
// Initialize the view holder
headerViewHolder.topicView = (TextView) view.findViewById(R.id.topic);
view.setTag(headerViewHolder);
final View imageView = view.findViewById(R.id.more_icon);
imageView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Start the Fragement
}
});
Topic topic = header.getTopic();
if(topic.isKeyword()) {
headerViewHolder.topicView.setText(topic.getName());
}
else {
// This is a hack to avoid error with - in android
headerViewHolder.topicView.setText(ResourceUtils.getStringByName(context, topic.getName()));
}
return view;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
private ImageView mImageView;
private String url;
private String dir;
private String filename;
public DownloadImageTask(ImageView imageView, String url, String dir, String filename) {
this.mImageView = imageView;
this.url = url;
this.filename = filename;
this.dir = dir;
}
protected Bitmap doInBackground(String... urls) {
final Bitmap mIcon11 = FileUtils.readImage(context, dir, filename, url);
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
//final ImageCache cache = ImageCache.getInstance();
if(result != null) {
mCache.put(url, result);
mImageView.setImageBitmap(result);
}
}
}
static class EntryViewHolder {
TextView titleView;
TextView timeView;
TextView propsView;
ImageView imageView;
TextView topicView;
}
static class HeaderViewHolder {
TextView topicView;
}
}
更新3:我附加了來自eclipse的調試信息:1 st圖片是工作位圖,第二個是來自緩存的非工作位圖。我沒有發現任何可疑的東西。
從緩存工作位圖的調試信息:
非工作位圖的從高速緩存中的調試信息:
非常感謝greywolf!我會嘗試這個,但不知何故,有沒有辦法讓全局緩存?如果我在緩存中使用片段,那麼將需要2個緩存(因爲我有2個片段),許多位圖將被緩存兩次。這是我想避免的。 –
我將單例更改爲普通類,並將其實例傳遞給片段。仍然有同樣的問題。 –
你可以擴展Application類並把你的單例作爲選擇。你可以發佈你調用cacheImage的部分代碼嗎? – greywolf82