ListView异步加载 LruCache缓存 滑动状态监听

Android异步加载的总结。除了LruCache,我们还可以使用DiskLruCache这个第三方的类来实现“二级缓存”。异步加载不仅仅是获取网络资源,我们可以加所有耗时的操作都看成异步加载,所有通过耗时的操作获取的结果我们都可以通过缓存来提高效率,比如我们可以利用这里的LruCache和DiskLruCache(这个缓存一般是用于缓存从网络获取的图片,视频等大的资源,这个时候我们一般和LruCache结合使用),使用Cache有个缺点,就是我们不能保证我们的数据是实时的,所以在realtime需求高的应用中,我们不应该使用缓存。而对于本地资源,我们很少用缓存的,就算是操作SQLite数据库,我们也很少会用到LruCache,但是也有使用的。

listview的优化

1. 复用convertView,减少item view 的加载次数

2.使用holder,减少获取item中子控件的次数

3.使用异步加载,防止阻塞主线程

4.使用google提供的LruCache类,将图片缓存到内存中,减少从网络获取图片的次数

加快速度,节省流量。lru 算法

5. 防止图面缓存引起的错位问题,给每个imageView添加一个tag,用URL做标识,加载图片时

只有当前的URL和要加载图片的imageView的tag中的URL相同时,才加载.

6.监听listView的滚动状态。滚动时停止记载,记录当前可见item的起始和结束位置,并记录他们的url,滚动结束后载再加载。 防止滑动中加载数据造成卡顿,和不必要的流量浪费.

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.LruCache;
import android.widget.ImageView;
import android.widget.ListView;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by Aldrich_jia on 2016-5-9.
 */
public class ImageLoader {
    private ImageView mImageView;
    private String iconUrl;
    //创建cache
    private LruCache<String, Bitmap> mCaches;
    private ListView mListView;
    private Set<NewsIconAsyncTask> mTask;

    public ImageLoader(ListView mListView) {
        this.mListView = mListView;
        mTask = new HashSet<>();
        //获取最大可用内存
        int maxMemory = (int) Runtime.getRuntime().maxMemory();
        //设置换成大小值
        int cacheSize = maxMemory / 4;
        mCaches = new LruCache<String, Bitmap>(cacheSize) {
            //告诉系统,每次存入缓存多大
            @Override
            protected int sizeOf(String key, Bitmap value) {
                //每次存入缓存时调用
                return value.getByteCount();
            }
        };
    }

    //从缓存中获取数据
    public Bitmap getBitmapfromCache(String url) {
        return mCaches.get(url);
    }

    //增加到缓存
    private void addBitmaptoCache(String url, Bitmap bitmap) {
        if (getBitmapfromCache(url) == null) {
            mCaches.put(url, bitmap);
        }
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (mImageView.getTag().equals(iconUrl)) {
                mImageView.setImageBitmap((Bitmap) msg.obj);
            }
        }
    };

    public void showImageByThread(ImageView imageView, final String urlString) {
        this.mImageView = imageView;
        this.iconUrl = urlString;
        new Thread(new Runnable() {
            @Override
            public void run() {
                Bitmap mbitamp = getBitmapForURL(urlString);
                Message message = Message.obtain();
                message.obj = mbitamp;
                mHandler.sendMessage(message);
            }
        }).start();
    }

    private Bitmap getBitmapForURL(String urlString) {
        Bitmap mbitmap;
        InputStream is = null;
        try {
            URL url = new URL(urlString);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            is = new BufferedInputStream(urlConnection.getInputStream());
            mbitmap = BitmapFactory.decodeStream(is);
            urlConnection.disconnect();
            return mbitmap;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public void showImageByAsync(ImageView imageView, String mUrl) {
        //从缓存中取出对应的图片
        Bitmap bitmap = getBitmapfromCache(mUrl);
        //如果缓存中没有,必须去下载
        if (bitmap == null) {
            imageView.setImageResource(R.mipmap.ic_launcher);
//            new NewsIconAsyncTask(imageView, mUrl).execute(mUrl);
        } else {
            imageView.setImageBitmap(bitmap);
        }
    }

    /**
     * 显示从start到end中的图片
     *
     * @param start
     * @param end
     */
    public void loadImages(int start, int end) {
        for (int i = start; i < end; i++) {
            String url = NewsAdapter.URLS[i];
            //从缓存中取出对应的图片
            Bitmap bitmap = getBitmapfromCache(url);
            //如果缓存中没有,必须去下载
            if (bitmap == null) {
                NewsIconAsyncTask asyncTask = new NewsIconAsyncTask(url);
                asyncTask.execute(url);
                mTask.add(asyncTask);
            } else {
                ImageView imageView = (ImageView) mListView.findViewWithTag(url);
                imageView.setImageBitmap(bitmap);
            }
        }
    }

    public void cancelAllTask() {
        if (mTask != null) {
            for (NewsIconAsyncTask task : mTask) {
                task.cancel(false);
            }
        }
    }

    private class NewsIconAsyncTask extends AsyncTask<String, Void, Bitmap> {
        //        private ImageView mImageView;
        private String mUrl;

        public NewsIconAsyncTask(String mUrl) {
//            this.mImageView = mImageView;
            this.mUrl = mUrl;
        }

        @Override
        protected Bitmap doInBackground(String... strings) {
            String url = strings[0];
            //从网络上获取图片
            Bitmap bitmap = getBitmapForURL(url);
            if (bitmap != null) {
                //将不在缓存的图片加入缓存
                addBitmaptoCache(url, bitmap);
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            ImageView imageView = (ImageView) mListView.findViewWithTag(mUrl);
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            mTask.remove(this);
//            if (mImageView.getTag().equals(mUrl)) {
//                mImageView.setImageBitmap(bitmap);
//
//            }
        }
    }
}
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import java.util.List;

/**
 * Created by Aldrich_jia on 2016-5-9.
 */
public class NewsAdapter extends BaseAdapter implements AbsListView.OnScrollListener {
    private List<NewsBean> mList;
    private LayoutInflater mLayoutInflater;
    private ImageLoader mImageLoader;
    private int mStart, mEnd;
    public static String[] URLS;
    private boolean mFirstIn;

    public NewsAdapter(Context context, List<NewsBean> mList, ListView listView) {
        mLayoutInflater = LayoutInflater.from(context);
        this.mList = mList;
        mImageLoader = new ImageLoader(listView);
        URLS = new String[mList.size()];
        for (int i = 0; i < mList.size(); i++) {
            URLS[i] = mList.get(i).newsIconUrl;
        }

        listView.setOnScrollListener(this);
        mFirstIn = true;
    }

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

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

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

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHoloder viewHoloder = null;
        if (view == null) {
            viewHoloder = new ViewHoloder();
            view = mLayoutInflater.inflate(R.layout.item_layout, null);
            viewHoloder.ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
            viewHoloder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
            viewHoloder.tvContent = (TextView) view.findViewById(R.id.tv_content);
            view.setTag(viewHoloder);
        } else {
            viewHoloder = (ViewHoloder) view.getTag();
        }
        String iconUrl = mList.get(i).newsIconUrl;
        viewHoloder.ivIcon.setTag(iconUrl);
        viewHoloder.ivIcon.setImageResource(R.mipmap.ic_launcher);
//        new ImageLoader().showImageByThread(viewHoloder.ivIcon,iconUrl);
        mImageLoader.showImageByAsync(viewHoloder.ivIcon, iconUrl);
        viewHoloder.tvTitle.setText(mList.get(i).newsTitle);
        viewHoloder.tvContent.setText(mList.get(i).newsContent);

        return view;
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (scrollState == SCROLL_STATE_IDLE) {
            //加载可见项
            mImageLoader.loadImages(mStart, mEnd);
        } else {
            //停止任务
            mImageLoader.cancelAllTask();
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mStart = firstVisibleItem;
        mEnd = firstVisibleItem + visibleItemCount;
        //第一次显示的时候调用
        if (mFirstIn && visibleItemCount > 0) {
            mImageLoader.loadImages(mStart, mEnd);
            mFirstIn = false;
        }
    }

    class ViewHoloder {
        private TextView tvTitle, tvContent;
        private ImageView ivIcon;
    }
}
时间: 2024-11-05 14:57:16

ListView异步加载 LruCache缓存 滑动状态监听的相关文章

Android新浪微博客户端(七)——ListView中的图片异步加载、缓存

原文出自:方杰|http://fangjie.sinaapp.com/?p=193转载请注明出处 最终效果演示:http://fangjie.sinaapp.com/?page_id=54该项目代码已经放到github:https://github.com/JayFang1993/SinaWeibo 一.ListView的图片异步加载 我们都知道对每一个Weibo Item都有用户头像,而且每一条微博还可能带有图片.如果在加载列表的同时加载图片,这样有几个缺点,第一很费事,界面卡住,用户体验很不

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

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

Android进阶:ListView性能优化异步加载图片 使滑动效果流畅

ListView 是一种可以显示一系列项目并能进行滚动显示的 View,每一行的Item可能包含复杂的结构,可能会从网络上获取icon等的一些图标信息,就现在的网络速度要想保持ListView运行的很好滚动流畅是做不到的 所以这里就需要把这些信息利用多线程实现异步加载 实现这样功能的类 [java] view plaincopy public class AsyncImageLoader { private HashMap<String, SoftReference<Drawable>&

Android之ListView异步加载网络图片(优化缓存机制)【转】

网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决.下面提出一些优化: 1.采用线程池 2.内存缓存+文件缓存 3.内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4 4.对下载的图片进行按比例缩放,以减少内存的消耗 具体的代码里面说明.先放上内存缓存类的代码MemoryCache.java: public class MemoryCache { private static final String TAG = "MemoryCa

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

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

android listview 异步加载问题

============问题描述============ 学做android,自己想模仿QQ空间做一个小demo listview异步加载图片的时候遇到了一个问题 异步加载用到了SoftReference 和文件缓存的方法 每次加载图片的时候,也在内存或缓存中找到了图片 第一次加载出来后,listview滑动了,同样也进到了setImageBitmap这一步 可是就是图片显示不出来变成空白 下面帖几张图和代码 滑动前 滑动后 Image_url = new StringBuffer(AppCon

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异步加载图片错位,重复,闪烁分析以及解决方案

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