android - 异步任务的 Gridview 回收问题

标签 android android-view android-gridview recycle

当我在 GridView 中来回滚动时,我的图像没有正确回收,最终将在整个网格中显示相同的图像。

Recycling problem

适配器

public class ImageAdapter extends BaseAdapter {
    private Context mContext;
    private List<String> mList;
    private int mheight;
    private int mwidth;
    private Bitmap nBitmap;

    public ImageAdapter(Context context, List<String> list, int height, int width) {
        mContext = context;
        mList = list;
        mheight = height;
        mwidth = width;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        return mList.get(position).toString();
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView;
        if (convertView == null) {
            imageView = new ImageView(mContext);
            InputStream is;
            try {
                is = mContext.getAssets().open(mList.get(position));
                Bitmap bm = BitmapFactory.decodeStream(is);
                Bitmap mBitmap = Bitmap.createScaledBitmap(bm, mwidth / 3, mwidth / 3, false);
                imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);

                this.nBitmap = mBitmap;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else {
            imageView = (ImageView) convertView;
        }
        imageView.setImageBitmap(nBitmap);
        return imageView;
    }
}

我试图从快速滚动的 Ui 线程解码位图,但启动应用程序时缩略图被 1 个 1 加载,回收的缩略图 View 从原始 View 更改,在低内存设备上应用程序崩溃,同时从此处的 AsyncTask 加载缩略图已更新代码

public class ImageAdapter extends BaseAdapter {
private Context mContext;
private List<String> mList;
private int mheight;
private int mwidth;
private InputStream is;

public ImageAdapter(Context context, List<String> list, int height, int width) {
    mContext = context;
    mList = list;
    mheight = height;
    mwidth = width;
}


@Override
public int getCount() {
    return mList.size();
}

@Override
public Object getItem(int position) {
    return mList.get(position).toString();
}

@Override
public long getItemId(int position) {
    return 0;
}





@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView;
    if (convertView == null) {
        imageView = new ImageView(mContext);
    } else {
        imageView = (ImageView) convertView;
    }


    InputStream is;
    try {
        is = mContext.getAssets().open(mList.get(position));
        Loadimage task = new Loadimage(imageView , mheight , mwidth);
        task.execute(is);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return imageView ;

}
 public class Loadimage extends AsyncTask<InputStream, Void, Bitmap>{
private final WeakReference<ImageView> imageViewReference;


private InputStream is = null;
private int width;


public Loadimage(ImageView imageView, int mheight, int mwidth) {
     imageViewReference = new WeakReference<ImageView>(imageView);
     this.width=mwidth;

    // TODO Auto-generated constructor stub
}

@Override
protected Bitmap doInBackground(InputStream... params) {
    is = params[0];

    if (is !=null) {

        Bitmap bitmap  = BitmapFactory.decodeStream(is);
        Bitmap nBitmap =Bitmap.createScaledBitmap(bitmap,width/3 , width/3, false);
        return nBitmap;     
    }
    return null; 
  }
 @Override
 protected void onPostExecute(Bitmap bitmap) {
    if (imageViewReference != null && bitmap != null) {
        final ImageView imageView = imageViewReference.get();
        if (imageView != null) {
            imageView.setImageBitmap(bitmap);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        }


}

最佳答案

您需要将生成位图的 block 移到 if/then 之外。现在,您仅在 convertView == null 时生成新的位图。

我相信您的代码是正确的:

public class ImageAdapter extends BaseAdapter {
    private Context mContext;
    private List<String> mList;
    private int mheight;
    private int mwidth;

    public ImageAdapter(Context context, List<String> list, int height, int width) {
        mContext = context;
        mList = list;
        mheight = height;
        mwidth = width;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        return mList.get(position).toString();
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView;
        if (convertView == null) {
            imageView = new ImageView(mContext);
        } else {
            imageView = (ImageView) convertView;
        }

        InputStream is;
        try {
            is = mContext.getAssets().open(mList.get(position));
            Bitmap bm = BitmapFactory.decodeStream(is);
            Bitmap bitmap = Bitmap.createScaledBitmap(bm, mwidth / 3, mwidth / 3, false);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);

            imageView.setImageBitmap(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return imageView;
    }
}

你的滚动不会很流畅,你会得到很多丢帧,因为你的位图解码代码效率低下(没有缓存),而且这一切都发生在 UI 线程上,这不利于响应。当前代码中的瓶颈不是 ImageView 的分配和垃圾收集。

瓶颈(可能到目前为止)是您对 Bitmap 的处理和创建。

您可以查看以下链接以获取建议:

Displaying Bitmaps Efficiently

特别是这个:

Processing Bitmaps Off the UI Thread

关于android - 异步任务的 Gridview 回收问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15560008/

相关文章:

android - Android 订阅的订单 ID

android:contentInsetLeft ="0dp"与 app:contentInsetLeft ="0dp"

android - Android 应用中每个 View 的单独首选项

安卓 CMake : Could NOT find OpenSSL

android - 如何在 Android 中只给 RelativeLayout 下边框

java - 带有 unicode 字符或自定义字体的软键盘

android - 如何获取 View ID 的字符串表示形式

android - 用作 Google v3 map 缩略图的静态图像

android - onCreate() 方法未被调用

Android GridView 背景亮点