ListView перезаписывает Bitmap, не замечая в lrucache

Я пишу приложение для галереи. Это работает из шаблона androidstudio для фрагмента списка, с AbsList.

Я переопределяю getView, чтобы использовать задачу и lrucache для кэширования некоторых растровых изображений.

Каждое представление из списка - это RelativeLayout с ImageView над TextView. Если в кеше нет растрового изображения, AsyncTask загружает его и помещает в кеш, а getView рисует ресурс в ImageView. После загрузки onPostExecute помещает растровое изображение в ImageView.

Если в кеше имеется соответствующее растровое изображение, оно устанавливается в ImageView

Я установил объект, содержащий TextView и ImageView вместе с идентификатором в теге параметра convertView getView, чтобы отслеживать правильное растровое изображение для рисования.

У меня есть две проблемы:

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

Класс Cover имеет этот метод 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();
    }

На данный момент обложки не определены, список галерей просто загружается с заголовками, общим содержанием и данными идентификаторов. Изображения (обложки) загружаются в 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 ответ

Решение

Когда я прокручиваю вниз в первый раз, новые представления Изображения появляются с предыдущим растровым изображением на мгновение, прежде чем задача завершит настройку правильного растрового изображения (даже если я рисую растровое растровое изображение в getView адаптера), я не понимаю, почему.

Это должно быть потому, что вы положили notifyDataSetChanged() в неправильном месте. Пожалуйста, отправьте код, где вы положили его.

Когда я выполняю прокрутку назад, в большинстве случаев происходит сбой приложения, поскольку растровое изображение в кеше оказывается переработанным, хотя я понятия не имею, кто его переработал.

Я думаю, потому что вы не указываете, что делать, если paintCover является true:

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 код.

Другие вопросы по тегам