ListView在lrucache中回收了Bitmap而没有注意到

时间:2014-11-04 06:04:42

标签: android listview bitmap recycle

我正在写一个画廊应用程序。 它适用于列表片段的androidstudio模板,带有AbsList。

我重写getView以使用任务和lrucache来缓存一些位图。

listview中的每个视图都是一个RelativeLayout,在TextView上方有一个ImageView。 如果缓存中没有Bitmap,则AsyncTask会加载它并将其放入缓存中,getView会在ImageView上绘制资源。 加载后,onPostExecute将位图放入ImageView。

如果缓存上有相应的Bitmap,则将其设置为ImageView

我将一个持有TextView和ImageView的对象以及一个id放入getView的convertView参数标记中,以跟踪要绘制的正确位图。

但我有这两个问题:

  1. 当我第一次向下滚动时,在任务完成设置正确的位图之前,新的图像视图会显示前一个位图(即使我在适配器的getView上绘制了资源位图)不明白为什么。

  2. 当我向后滚动时,大多数时候应用程序崩溃,因为缓存上的位图最终会被回收,但我不知道是谁回收它。

  3. 任何人都可以帮我理解这里发生的事情吗?

    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;
            }
    

    Cover类有这个paintCover方法,其中mId是图像的uri / stream

    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;
        }
    
    }
    

    更多细节。 在Fragment的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;
        }
    }
    

1 个答案:

答案 0 :(得分:0)

  

第一次向下滚动时,新的图像视图显示为   在任务完成设置之前的瞬间的前一个位图   正确的位图(即使我在适配器上绘制资源Bitmap)   getView)我不明白为什么。

应该是因为你把notifyDataSetChanged()放在了错误的地方。请将代码发布到您的位置。

  

当我向后滚动时,大多数时候应用程序崩溃,因为Bitmap打开了   缓存最终被回收,但我不知道谁回收   它

我认为是因为如果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代码。