2014-12-29 106 views
1

我有一個使用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圖片是工作位圖,第二個是來自緩存的非工作位圖。我沒有發現任何可疑的東西。

從緩存工作位圖的調試信息:

The debug information of the working bitmap from the cache

非工作位圖的從高速緩存中的調試信息:

The debug information of the non-working bitmap from the cache

回答

3

最後我想出了問題。這是因爲適配器。在適配器中,如果不需要縮略圖,我已將某些ImageView設置爲不可見。當用戶滾動列表視圖時,此類ImageView實例將被重用,但可見性不會更新。

所以緩存本身就OK了。解決方案是檢查ImageView的可見性並在需要時更新它。

無論如何,非常感謝greywolf82爲您的時間和關於單身模式的提示。

1

單元素模式是邪惡:)請完全避免它,並使用與setReteainInstance(true)片段

+0

非常感謝greywolf!我會嘗試這個,但不知何故,有沒有辦法讓全局緩存?如果我在緩存中使用片段,那麼將需要2個緩存(因爲我有2個片段),許多位圖將被緩存兩次。這是我想避免的。 –

+0

我將單例更改爲普通類,並將其實例傳遞給片段。仍然有同樣的問題。 –

+1

你可以擴展Application類並把你的單例作爲選擇。你可以發佈你調用cacheImage的部分代碼嗎? – greywolf82