2014-11-04 55 views
1

我正在寫一個圖庫應用程序。 它使用androidstudio模板列表片段和AbsList。ListView在lrucache中沒有注意地回收位圖

我重寫getView以使用任務和lrucache來緩存一些位圖。

來自listview的每個視圖都是一個帶有一個ImageView上的TextView的RelativeLayout。 如果緩存中沒有位圖,則AsyncTask加載它並將其放入緩存,getView在ImageView上繪製資源。 加載後,onPostExecute將位圖放入ImageView中。

如果在高速緩存中的相應的位圖,將它設置成ImageView的

我設置對象持有的TextView和ImageView的隨着一個id進入getView的convertView參數標籤來跟蹤的正確的位圖繪製。

我有兩個問題,雖然:

  1. 當我第一次向下滾動,新形象的看法似乎與以前的位圖瞬間任務完成後設置正確的位圖(甚至前儘管我在適配器的getView上繪製資源位圖)但我不明白爲什麼。

  2. 當我向後滾動時,大多數時候應用程序崩潰,因爲緩存上的位圖原來被回收,儘管我不知道是誰回收了它。

任何人都可以幫我理解這裏會發生什麼嗎?

public View getView(int position, View convertView, ViewGroup parent) { 
      Log.i(TAG, "getView: Asking for view " + position); 
      GalleryItemViewHolder lViewHolder; 
      if (convertView == null) { 
       convertView = getActivity().getLayoutInflater().inflate(R.layout 
         .gallery_item, null); 
       lViewHolder = new GalleryItemViewHolder(); 
       convertView.setTag(lViewHolder); 

      } else { 
       lViewHolder = (GalleryItemViewHolder) convertView.getTag(); 
      } 
      lViewHolder.setId(position); 
      lViewHolder.setTextView((TextView) convertView.findViewById(R.id.gallery_infoTextView)); 
      lViewHolder.setImageView((ImageView) convertView.findViewById(R.id.gallery_imageView)); 

      lViewHolder.getTextView().setText(getItem(position).getName() + ": (" + getItem 
        (position).getCount() + ")"); 
      if (!getItem(position).paintCover(lViewHolder.getImageView())) { 
       Log.i(TAG,"getView: task"); 
       new GalleryItemTask(position, lViewHolder) 
         .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null); 
      } 
      Log.i(TAG,"getView: return"); 
      return convertView; 
     } 

封面類有此paintCover方法,其中MID是URI /流至圖像

public boolean paintCover(ImageView imageView) { 
    Bitmap lBitmap; 
    if (mId == null || (lBitmap = BitmapCacheManager.getInstance().get(mId)) == null) { 
     i(TAG, "paintCover: Sin Cache "); 
     imageView.getHeight(); 
     imageView.getWidth(); 
     imageView.setImageResource(android.R.drawable.alert_dark_frame); 
     return false; 

    } else 

    { 
     i(TAG, "paintCover: En Cache "+lBitmap.isRecycled()); 
     imageView.setImageBitmap(lBitmap); 
     return true; 
    } 

} 

的更多細節。 在片段的onCreate,我跑這個方法:

private void prepareGalleryLoaders() { 
    LoaderManager lm = getLoaderManager(); 
    Log.i(TAG, "prepareGalleryLoaders: Iniciando loader"); 
    lm.initLoader(IdConstants.GALLERY_LOADER, null, new GalleryLoaderCallbacks()); 
} 



/** 
* Callbacks para cargar los datos de las galerías 
* Al terminar de cargarlas, se crea el nuevo arreglo 
*/ 
private class GalleryLoaderCallbacks implements LoaderManager.LoaderCallbacks<List<Gallery>> { 
    @Override 
    public Loader<List<Gallery>> onCreateLoader(int id, Bundle args) { 

     return new GalleriesLoader(getActivity()); 
    }private class GalleryItemTask extends AsyncTask<Void, Void, Gallery> { 
    private static final String TAG = "GalleryItemTask"; 
    private int mId; 
    private String mCoverId; 
    private GalleryItemViewHolder mViewHolder; 
    private Bitmap mBitmap; 

    GalleryItemTask(int id, GalleryItemViewHolder galleryItemViewHolder) { 
     mViewHolder = galleryItemViewHolder; 
     mId = id; 
    } 



    @Override 
    protected void onPostExecute(Gallery galleries) { 

     if (mId != mViewHolder.getId()) { 
      Log.i(TAG, "onPostExecute: IDs difieren!!! "+mId+" - "+mViewHolder.getId()); 
      mBitmap.recycle(); 
      mBitmap=null; 
      return; 
     } 
     // Validar y actualizar bitmap 


     mViewHolder.getImageView().setImageBitmap(mBitmap); 
     //mGalleries.get(mId).setBitmap(mBitmap); 

     super.onPostExecute(galleries); 
    } 

    @Override 
    protected Gallery doInBackground(Void... params) { 
     // generar bitmap (y posiblemente agregarlo a algún cache) 


     String[] queryProjection = { 
       MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.TITLE}; 
     String[] selectionArgs = new String[]{String.valueOf(mGalleries.get(mId).getId())}; 
     Cursor lCursor = getView().getContext().getContentResolver().query(
       MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
       queryProjection, MediaStore.Images.ImageColumns.BUCKET_ID + "= ?", 
       selectionArgs, MediaStore.Images.ImageColumns.TITLE); 
     lCursor.moveToFirst(); 
     while (!lCursor.isAfterLast()) { 
      //Log.i(TAG,"doInBackground: "+mGalleries.get(mId).getName()+" - "+lCursor.getString 
      //  (1)+" - "+ lCursor.getString(0)); 
      lCursor.moveToNext(); 
     } 
     lCursor.moveToFirst(); 
     Log.i(TAG, "doInBackground: " + mId + " - " + mViewHolder.getId()); 

     BitmapFactory.Options lOptions = new BitmapFactory.Options(); 
     lOptions.inJustDecodeBounds = true; 
     mBitmap = BitmapFactory.decodeFile(lCursor.getString(0), lOptions); 
     lOptions.inSampleSize = ImageUtils.calculateInSampleSize(lOptions, 256, 256); 
     lOptions.inJustDecodeBounds = false; 
     mBitmap = BitmapFactory.decodeFile(lCursor.getString(0), lOptions); 

     BitmapCacheManager.getInstance().put(lCursor.getString(0), mBitmap); 


     //if(mGalleries.get(mId).getBitmap()!=null) 
     // mGalleries.get(mId).getBitmap().recycle(); 
     //mGalleries.get(mId).setBitmap(mBitmap); 



     if(!mGalleries.get(mId).hasCover()) { 
      SimpleCover lSimpleCover=new SimpleCover(getActivity(),lCursor.getString(0)); 
      mGalleries.get(mId).setCover(lSimpleCover); 
     } 
     lCursor.close(); 
     return null; 
    } 
} 


    @Override 
    public void onLoadFinished(Loader<List<Gallery>> loader, List<Gallery> data) { 
     if (mGalleries != null) { 
      mGalleries.clear(); 

     } else 
      mGalleries = new ArrayList<Gallery>(); 
     mGalleries.addAll(data); 
     for (Gallery lGallery : data) { 
      Log.i(TAG, "onLoadFinished: " + lGallery.getName()); 
     } 

     mAdapter.notifyDataSetChanged(); 
    } 

在這一點上,目前還沒有定義封面,只是加載了冠軍和總含量和id數據畫廊名單。圖像(封面)從列表適配器的getView中加載。

的GalleryItemTask類:

private class GalleryItemTask extends AsyncTask<Void, Void, Gallery> { 
    private static final String TAG = "GalleryItemTask"; 
    private int mId; 
    private String mCoverId; 
    private GalleryItemViewHolder mViewHolder; 
    private Bitmap mBitmap; 

    GalleryItemTask(int id, GalleryItemViewHolder galleryItemViewHolder) { 
     mViewHolder = galleryItemViewHolder; 
     mId = id; 
    } 



    @Override 
    protected void onPostExecute(Gallery galleries) { 

     if (mId != mViewHolder.getId()) { 
      Log.i(TAG, "onPostExecute: IDs difieren!!! "+mId+" - "+mViewHolder.getId()); 
      mBitmap.recycle(); 
      mBitmap=null; 
      return; 
     } 
     // Validar y actualizar bitmap 


     mViewHolder.getImageView().setImageBitmap(mBitmap); 
     //mGalleries.get(mId).setBitmap(mBitmap); 

     super.onPostExecute(galleries); 
    } 

    @Override 
    protected Gallery doInBackground(Void... params) { 
     // generar bitmap (y posiblemente agregarlo a algún cache) 


     String[] queryProjection = { 
       MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.TITLE}; 
     String[] selectionArgs = new String[]{String.valueOf(mGalleries.get(mId).getId())}; 
     Cursor lCursor = getView().getContext().getContentResolver().query(
       MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
       queryProjection, MediaStore.Images.ImageColumns.BUCKET_ID + "= ?", 
       selectionArgs, MediaStore.Images.ImageColumns.TITLE); 
     lCursor.moveToFirst(); 
     while (!lCursor.isAfterLast()) { 
      //Log.i(TAG,"doInBackground: "+mGalleries.get(mId).getName()+" - "+lCursor.getString 
      //  (1)+" - "+ lCursor.getString(0)); 
      lCursor.moveToNext(); 
     } 
     lCursor.moveToFirst(); 
     Log.i(TAG, "doInBackground: " + mId + " - " + mViewHolder.getId()); 

     BitmapFactory.Options lOptions = new BitmapFactory.Options(); 
     lOptions.inJustDecodeBounds = true; 
     mBitmap = BitmapFactory.decodeFile(lCursor.getString(0), lOptions); 
     lOptions.inSampleSize = ImageUtils.calculateInSampleSize(lOptions, 256, 256); 
     lOptions.inJustDecodeBounds = false; 
     mBitmap = BitmapFactory.decodeFile(lCursor.getString(0), lOptions); 

     BitmapCacheManager.getInstance().put(lCursor.getString(0), mBitmap); 


     //if(mGalleries.get(mId).getBitmap()!=null) 
     // mGalleries.get(mId).getBitmap().recycle(); 
     //mGalleries.get(mId).setBitmap(mBitmap); 



     if(!mGalleries.get(mId).hasCover()) { 
      SimpleCover lSimpleCover=new SimpleCover(getActivity(),lCursor.getString(0)); 
      mGalleries.get(mId).setCover(lSimpleCover); 
     } 
     lCursor.close(); 
     return null; 
    } 
} 

回答

0

當我第一次向下滾動,新形象的看法似乎與任務前 以前位瞬間完設立 正確的位圖(即使盡管我在適配器的 getView上繪製資源位圖)我不明白爲什麼。

這應該是因爲你把notifyDataSetChanged()放在了錯誤的地方。請將代碼放在你放置的地方。

當我向後滾動,次數最多的應用程序崩潰,因爲在 緩存原來的位圖被回收,雖然我不知道誰回收 它。

我認爲它的,因爲你沒有指定做什麼,如果paintCovertrue

if (!getItem(position).paintCover(lViewHolder.getImageView())) { 
     Log.i(TAG,"getView: task"); 
     new GalleryItemTask(position, lViewHolder) 
       .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null); 
} 
else 
{ 
    //what should the adapter do if paintCover is true? 
} 

如果錯誤仍然存​​在,請發表您的GalleryItemTask代碼。

+0

如果paintCover返回false,則表示它在ImageView上繪製了一個模板,然後線程運行以創建Bitmap並將其放入緩存中,並在稍後將其繪製在ImageView上。如果它返回true,則表示圖像在緩存中找到,並放入ImageView中,因此沒有任何操作。然而,在試圖找出之後,我發現緩存中的圖像被回收了,但我不知道是誰做的或者它是如何發生的。 – dabicho 2014-11-04 07:03:26

+0

如果paintCover爲真,你想要做什麼? – 2014-11-04 07:07:21

+0

謝謝,我剛剛發現我做錯了什麼......如果ID不同,我錯誤地回收了位圖,因爲我已經將它放入緩存。我的錯。但不知何故,觀看/討論這裏的代碼對我來說很清楚。如果paintCover爲true,則意味着封面已經塗上了最終的Bitmap(因爲它在緩存中找到),所以暫時沒有。 – dabicho 2014-11-04 07:10:47