team agam team agam - 12 days ago 6
Java Question

WeakReference to a RecyclerView's View never breaks?

I have a weakreference to a recyclerview's view in my asynctask (the task loads an image to show in the view). Normally (if I use a listview) the view is collected by the gc when it disappears so the task won't use it, but in my case (with the recyclerview) the view is never collected by the gc so the task yet has its weakreference pointing to the view, which now has a new role (another image to show). How do I solve it? Am I right with my understanding of the situation?

Thanks!

I hope u understand though english is not my native...

Edit:
This is the code for the AsyncTask:

class LoadPictureTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> mImageViewReference;
private final int mImageSize;

LoadPictureTask(ImageView imageView, int imageSize) {
// Use a WeakReference to ensure the ImageView can be garbage collected
mImageViewReference = new WeakReference<>(imageView);
mImageSize = imageSize;
}

@Override
protected Bitmap doInBackground(String... params) {
String path = params[0];
return decodeSampledBitmapFromFile(path, mImageSize, mImageSize);
}

@Override
protected void onPostExecute(Bitmap bitmap) {
if (mImageViewReference.get() != null && bitmap != null) {
final ImageView imageView = mImageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
...
}


My fear is that in onPostExecute I set another imageview with the resulting bitmap (a recycled one)

Answer

So I tested the above case and indeed the views' references were totally abused. The AsyncTask put images to wrong views and everything was really messed up. I solved this by saving a pointer to the AsyncTask from the ViewHolder, and then in onBindViewHolder I cancelled the previously executed AsyncTask and disabled the visibilty of the view until it was set to visible again by the new AsyncTask.

The code will clarify my intentions:

package com.teamagam.dailyselfie;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

class PictureAdapter extends RecyclerView.Adapter<PictureAdapter.PictureViewHolder> {
    private LayoutInflater mInflater;
    private List<PictureInfo> mPictureInfoList;

    PictureAdapter(Context context, List<PictureInfo> pictureInfoList) {
        mInflater = LayoutInflater.from(context);
        mPictureInfoList = pictureInfoList;
    }

    @Override
    public PictureViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.layout_picture, parent, false);
        return new PictureViewHolder(view);
    }

    @Override
    public void onBindViewHolder(PictureViewHolder pictureViewHolder, int position) {
        PictureInfo pictureInfo = mPictureInfoList.get(position);
        pictureViewHolder.mTextView.setText(pictureInfo.fileName);
        int imageSize = pictureViewHolder.mImageView
                .getContext()
                .getApplicationContext()
                .getResources()
                .getInteger(R.integer.activity_main_thumbnail_size);
        if (null != pictureViewHolder.mLoadPictureTask) {
            pictureViewHolder.mLoadPictureTask.cancel(true);
        }
        pictureViewHolder.mImageView.setVisibility(View.INVISIBLE);
        pictureViewHolder.mLoadPictureTask = new LoadPictureTask(pictureViewHolder.mImageView, imageSize);
        pictureViewHolder.mLoadPictureTask.execute(pictureInfo.path);
    }

    @Override
    public int getItemCount() {
        return mPictureInfoList.size();
    }

    class PictureViewHolder extends RecyclerView.ViewHolder {
        ImageView mImageView;
        TextView mTextView;
        LoadPictureTask mLoadPictureTask;

        PictureViewHolder(View itemView) {
            super(itemView);
            mImageView = (ImageView) itemView.findViewById(R.id.ivItemListImage);
            mTextView = (TextView) itemView.findViewById(R.id.tvItemListImage);
        }
    }
}

And of course the code for the AsyncTask:

package com.teamagam.dailyselfie;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.AsyncTask;
import android.view.View;
import android.widget.ImageView;

import java.lang.ref.WeakReference;

class LoadPictureTask extends AsyncTask<String, Void, Bitmap> {
    private final WeakReference<ImageView> mImageViewReference;
    private final int mImageSize;

    LoadPictureTask(ImageView imageView, int imageSize) {
        // Use a WeakReference to ensure the ImageView can be garbage collected
        mImageViewReference = new WeakReference<>(imageView);
        mImageSize = imageSize;
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        String path = params[0];
        return decodeSampledBitmapFromFile(path, mImageSize, mImageSize);
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (mImageViewReference.get() != null && bitmap != null) {
            final ImageView imageView = mImageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
                imageView.setVisibility(View.VISIBLE);
            }
        }
    }

    private static Bitmap decodeSampledBitmapFromFile(String path, int requiredWidth, int requiredHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final Options options = new Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateSampleSize(options, requiredWidth, requiredHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(path, options);
    }

    private static int calculateSampleSize(Options options, int requiredWidth, int requiredHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int sampleSize = 1;

        if (height > requiredHeight || width > requiredWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest sampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / sampleSize) >= requiredHeight
                    && (halfWidth / sampleSize) >= requiredWidth) {
                sampleSize *= 2;
            }
        }

        return sampleSize;
    }
}
Comments