android listview 异步加载图片并防止错位+双缓存

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作.

如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题。

我简单分析一下:

当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView.

当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Item8 复用的是

Item1 的 view 如果没有异步不会有任何问题,虽然 Item8 和 Item1 指向的是同一个 view,但滑到

Item8 时刷上了 Item8 的数据,这时 Item1 的数据和 Item8 是一样的,因为它们指向的是同一块内存,

但 Item1 已滚出了屏幕你看不见。当 Item1 再次可见时这块 view 又涮上了 Item1 的数据。

但当有异步下载时就有问题了,假设 Item1 的图片下载的比较慢,Item8 的图片下载的比较快,你滚上去

使 Item8 可见,这时 Item8 先显示它自己下载的图片没错,但等到 Item1 的图片也下载完时你发现

Item8 的图片也变成了 Item1 的图片,因为它们复用的是同一个 view。 如果 Item1 的图片下载的比

Item8 的图片快, Item1 先刷上自己下载的图片,这时你滑下去,Item8 的图片还没下载完, Item8

会先显示 Item1 的图片,因为它们是同一快内存,当 Item8 自己的图片下载完后 Item8 的图片又刷成

了自己的,你再滑上去使 Item1 可见, Item1 的图片也会和 Item8 的图片是一样的,

因为它们指向的是同一块内存。

最简单的解决方法就是网上说的,给 ImageView 设置一个 tag, 并预设一个图片。

当 Item1 比 Item8 图片下载的快时, 你滚下去使 Item8 可见,这时 ImageView 的 tag 被设成了

Item8 的 URL, 当 Item1 下载完时,由于 Item1 不可见现在的 tag 是 Item8 的 URL,所以不满足条件,

虽然下载下来了但不会设置到 ImageView 上, tag 标识的永远是可见 view 中图片的 URL。

关键代码如下:

public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;

    if (convertView == null) {
        holder = new ViewHolder();
        convertView = LayoutInflater.from(context).inflate(
                R.layout.list_item, null);
        holder.img = (ImageView) convertView.findViewById(R.id.userimage);

        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    User user = list.get(position);

    // 给 ImageView 设置一个 tag
    holder.img.setTag(user.getImgUrl());
    // 预设一个图片
    holder.img.setImageResource(R.drawable.ic_launcher);

    final String tmpImageUrl = user.getImgUrl();

    if (user.getImgUrl() != null && !user.getImgUrl().equals("")) {
        Bitmap bitmap = imageLoader.loadImage(holder.img, user.getImgUrl(),
                new ImageDownloadCallBack() {

                    @Override
                    public void onImageDownloaded(ImageView imageView,
                            Bitmap bitmap) {
                        // 通过 tag 来防止图片错位
                        if (imageView.getTag() != null
                                && imageView.getTag().equals(tmpImageUrl)) {
                            imageView.setImageBitmap(bitmap);
                        }
                    }
                });

        if (bitmap != null) {
            holder.img.setImageBitmap(bitmap);
        }
    }

    return convertView;
}

我参考网上资料写了一个 listview 异步加载图片的 DEMO:

(1) 使用线程池

没有线程池,当图片非常多,快速滑动  listview 时由于下载每个图片都开了一个线程,

可能出现 OOM (out of memory)。

(2) 内存、文件双缓存

这里也使用 SoftReference 软引用  

/**
 * 图片异步加载类
 *
 * @author Leslie.Fang
 * @company EnwaySoft
 *
 */
public class AsyncImageLoader {
    // 最大线程数
    private static final int MAX_THREAD_NUM = 10;
    private Map<String, SoftReference<Bitmap>> imageCaches = null;
    private FileUtil fileUtil;
    // 线程池
    private ExecutorService threadPools = null;

    public AsyncImageLoader(Context context) {
        imageCaches = new HashMap<String, SoftReference<Bitmap>>();
        fileUtil = new FileUtil(context);
    }

    public Bitmap loadImage(final ImageView imageView, final String imageUrl,
            final ImageDownloadCallBack imageDownloadCallBack) {
        final String filename = imageUrl
                .substring(imageUrl.lastIndexOf("/") + 1);
        final String filepath = fileUtil.getAbsolutePath() + "/" + filename;

        // 先从软引用中找
        if (imageCaches.containsKey(imageUrl)) {
            SoftReference<Bitmap> reference = imageCaches.get(imageUrl);
            Bitmap bitmap = reference.get();

            // 软引用中的 Bitmap 对象可能随时被回收
            // 如果软引用中的 Bitmap 已被回收,则从文件中找
            if (bitmap != null) {
                Log.i("aaaa", "cache exists " + filename);

                return bitmap;
            }
        }

        // 从文件中找
        if (fileUtil.isBitmapExists(filename)) {
            Log.i("aaaa", "file exists " + filename);
            Bitmap bitmap = BitmapFactory.decodeFile(filepath);

            // 重新加入到内存软引用中
            imageCaches.put(imageUrl, new SoftReference<Bitmap>(bitmap));

            return bitmap;
        }

        // 软引用和文件中都没有再从网络下载
        if (imageUrl != null && !imageUrl.equals("")) {
            if (threadPools == null) {
                threadPools = Executors.newFixedThreadPool(MAX_THREAD_NUM);
            }

            final Handler handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 111 && imageDownloadCallBack != null) {
                        Bitmap bitmap = (Bitmap) msg.obj;
                        imageDownloadCallBack.onImageDownloaded(imageView,
                                bitmap);
                    }
                }
            };

            Thread thread = new Thread() {
                @Override
                public void run() {
                    Log.i("aaaa", Thread.currentThread().getName()
                            + " is running");
                    InputStream inputStream = HTTPService.getInstance()
                            .getStream(imageUrl);
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);

                    // 图片下载成功重新缓存并执行回调刷新界面
                    if (bitmap != null) {
                        // 加入到软引用中
                        imageCaches.put(imageUrl, new SoftReference<Bitmap>(
                                bitmap));
                        // 缓存到文件系统
                        fileUtil.saveBitmap(filepath, bitmap);

                        Message msg = new Message();
                        msg.what = 111;
                        msg.obj = bitmap;
                        handler.sendMessage(msg);
                    }
                }
            };

            threadPools.execute(thread);
        }

        return null;
    }

    public void shutDownThreadPool() {
        if (threadPools != null) {
            threadPools.shutdown();
            threadPools = null;
        }
    }

    /**
     * 图片下载完成回调接口
     *
     */
    public interface ImageDownloadCallBack {
        void onImageDownloaded(ImageView imageView, Bitmap bitmap);
    }
}

DEMO下载

android listview 异步加载图片并防止错位+双缓存

时间: 2024-10-01 08:28:49

android listview 异步加载图片并防止错位+双缓存的相关文章

android listview 异步加载图片并防止错位

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

android listview 异步加载图片并防止错位 解决办法

网上找了一张图, listview 异步加载图片之所以错位的根本原因是重用了 convertView 且有异步操作. 如果不重用 convertView 不会出现错位现象, 重用 convertView 但没有异步操作也不会有问题. 我简单分析一下: 当重用 convertView 时,最初一屏显示 7 条记录, getView 被调用 7 次,创建了 7 个 convertView. 当 Item1 划出屏幕, Item8 进入屏幕时,这时没有为 Item8 创建新的 view 实例, Ite

又优化了一下 Android ListView 异步加载图片

写这篇文章并不是教大家怎么样用listview异步加载图片,因为这样的文章在网上已经有很多了,比如这位仁兄写的就很好: http://www.iteye.com/topic/685986 我也是因为看了这篇文章而受到了启发. 先说说这篇文章的优点把,开启线程异步加载图片,然后刷新UI显示图片,而且通过弱引用缓存网络加载的图片,节省了再次连接网络的开销. 这样做无疑是非常可取的方法,但是加载图片时仍然会感觉到轻微的卡屏现象,特别是listview里的item在进行快速滑动的时候. 我找了一下原因,

Android ListView异步加载图片乱序问题,原因分析及解决方案

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/45586553 在Android所有系统自带的控件当中,ListView这个控件算是用法比较复杂的了,关键是用法复杂也就算了,它还经常会出现一些稀奇古怪的问题,让人非常头疼.比如说在ListView中加载图片,如果是同步加载图片倒还好,但是一旦使用异步加载图片那么问题就来了,这个问题我相信很多Android开发者都曾经遇到过,就是异步加载图片会出现错位乱序的情况.遇到这个问题时,不

android listview异步加载图片错位,重复,闪烁分析以及解决方案

我们在使用listview异步加载图片 的时候,在快速滑动或者网络不好的情况下,会出现图片错位,重复,闪烁等问题,其实这些问题总结起来就是一个问题, 比如listview上有100个item,一屏只显示10个item,我们知道getView()中converView是用来复用view对象的,因为一个item的view对象,而imageview控件就是view通过findViewById()获得的,而我们在复用view对象时,也就是说这个imageview也被复用了,比如第11个item的view

Android ListView异步加载图片错位、重复、闪烁分析以及解决方案

我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而ImageView控件就是View对象通过findViewById()获得的,而我们在复用View对象时,同时这个Ima

android开发干货:实现listview异步加载图片

针对listview异步加载图片这个问题,麦子学院android开发老师讲了一种非常实用的方法,麦子学院android开发老师说凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法,先贴上主方法的代码: package cn.wangmeng.test; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.

Android之ListView异步加载图片且仅显示可见子项中的图片

折腾了好多天,遇到 N 多让人崩溃无语的问题,不过今天终于有些收获了,这是实验的第一版,有些混乱,下一步进行改造细分,先把代码记录在这儿吧. 网上查了很多资料,发现都千篇一律,抄来抄去,很多细节和完整实例都没看到,只有自己一点点研究了,总体感觉 android 下面要显示个图片真不容易啊. 项目主要实现的功能: 异步加载图片图片内存缓存.异步磁盘文件缓存解决使用 viewHolder 后出现的图片错位问题优化列表滚动性能,仅显示可见子项中的图片无需固定图片显示高度,对高度进行缓存使列表滚动时不会

android实用技巧:android实现listview异步加载图片

针对listview异步加载图片这个问题,麦子学院android开发老师讲了一种非常实用的方法,麦子学院android开发老师说凡是是要通过网络获取图片资源一般使用这种方法比较好,用户体验好,下面就说实现方法,先贴上主方法的代码: package cn.wangmeng.test; import java.io.IOException; import java.io.InputStream; import java.lang.ref.SoftReference; import java.net.