详解ListView加载网络图片的优化
我们来了解一些ListView在加载大量网络图片的时候存在的常见问题:
1.性能问题,ListView的滑动有卡顿,不流畅,造成非常糟糕的用户体验。
2.图片的错位问题。
3.图片太大,加载Bitmap时造成的OOM(Out of memory),也就是栈内存溢出。
4.异步线程丢失的问题。
针对所存在的问题我们逐个击破,彻底的掌握ListView的优化问题,有利于我们的学习和工作。
(一) 性能问题:
在这个问题上我们可以在Adapter适配器中中复用convertView 和写一个内部ViewHolder类来解决。但是如果每个Adapter中都写一个ViewHolder类会显得非常的麻烦,下面我给大家一个万能的ViewHolder类,方便在任何Adapter中调用。
调用BaseViewHolder类的示例代码:
<span style="font-size:14px;">    if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(
                    R.layout.personplans_item, parent, false);
        }  
        TextView tv_product_type1 = BaseViewHolder.get(convertView,
                R.id.tv_product_type1);  </span>
注意:在BaseViewHolder类中我们看到SparseArray是Android提供的一个工具类 ,用意是用来取代HashMap工具类的。如下图:
SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的类,目的是提高效率。具体如何提高效率可以去Android文档查询一下,这里就不赘述了。
(二)图片错位问题
这个问题导致的原因是因为复用ConvertView导致的,在加载大量的Item时,常见的错位问题。这种问题的解决思路通常是以图片的Url做为唯一的key,然后setTag中,然后获取时根据图片的URL来获得图片。
(三)防止OOM,以及异步加载。
关于异步加载图片的思路是:
1.第一次进入时,是没有图片的,这时候我们会启动一个线程池,异步的从网上获得图片数据,为了防止图片过大导致OOM,可以调用BitmapFactory中的Options类对图片进行适当的缩放,最后再显示主线程的ImageView上。
2.把加载好的图片以图片的Url做为唯一的key存入内存缓存当中,并严格的控制好这个缓存的大小,防止OOM的发生。
3.把图片缓存在SD当中,如果没有SD卡就放在系统的缓存目录cache中,以保证在APP退出后,下次进来能看到缓存中的图片,这样就可以让使你的APP不会给客户呈现一片空白的景象。
4.用户第二次进来的时候,加载图片的流程则是倒序的,首先从内容中看是否存在缓存图片,如果没有就从SD卡当中寻找,再没有然后才是从网络中获取图片数据。这样做的既可以提高加载图片的效率,同时也节约了用户的流量。
说完了理论性的东西,我们来开始动手实战一下吧,下面介绍一个GitHub上一个很轻巧的开源框架LazyListGitHub地址,然后基于它做一些我们想要的效果,关于开源的东西,我们不止要学会用,还要从中能学到东西。众所周知的Android-Universal-Image-Loader其实就是基于LazyList的一个拓展 ,增加了更多的配置。但是从学习的角度我们还是希望能从原理学起,太多的功能封装,难免会让我们晕乎,简单的功能实现就够了。
1.先来看一下运行效果图:
2.来看一下LazyList项目的结构:
构非常的简单,整个项目的体积才10多k,适合加入我们自己的项目当中去,研究起来也不会觉得难,因为都是最为核心的东西。
3.调用Lazylist的入口:
<span style="font-size:14px;"> adapter = new LazyAdapter(this, mStrings);
list.setAdapter(adapter); </span>
传入一个装满图片Url地址的字符串数组进去,然后在LazyAdapter中对ListView中的进行显示。
4.具体LazyAdapter中调用的代码是:
<span style="font-size:14px;">    public class LazyAdapter extends BaseAdapter {  
        private Activity activity;
        private String[] data;
        private static LayoutInflater inflater=null;
        public ImageLoader imageLoader;   
        public LazyAdapter(Activity a, String[] d) {
            activity = a;
            data=d;
            inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            imageLoader=new ImageLoader(activity.getApplicationContext());
        }  
        public int getCount() {
            return data.length;
        }  
        public Object getItem(int position) {
            return position;
        }  
        public long getItemId(int position) {
            return position;
        }  
        public View getView(int position, View convertView, ViewGroup parent) {
            View vi=convertView;
            if(convertView==null)
                vi = inflater.inflate(R.layout.item, null);  
               ImageView image=BaseViewHolder.get(vi, R.id.image);
               ImageView image2=BaseViewHolder.get(vi, R.id.image2);
               imageLoader.DisplayImage(data[position], image);
               imageLoader.DisplayImage(data[position], image2);
            return vi;
        }
    }  </span>
5.从上面我们可以看出来其实最重要的封装显示图片的方法就在ImageLoader这个类中。
<span style="font-size:14px;">    public class ImageLoader {  
        MemoryCache memoryCache=new MemoryCache();
        FileCache fileCache;
        private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
        ExecutorService executorService;
        Handler handler=new Handler();//handler to display images in UI thread  
        public ImageLoader(Context context){
            fileCache=new FileCache(context);
            executorService=Executors.newFixedThreadPool(5);
        }
        // 当进入listview时默认的图片,可换成你自己的默认图片
        final int stub_id=R.drawable.stub;
        public void DisplayImage(String url, ImageView imageView)
        {
            imageViews.put(imageView, url);
            // 先从内存缓存中查找
            Bitmap bitmap=memoryCache.get(url);
            if(bitmap!=null)
                imageView.setImageBitmap(bitmap);
            else
            {
                 // 若没有的话则开启新线程加载图片
                queuePhoto(url, imageView);
                imageView.setImageResource(stub_id);
            }
        }  
        private void queuePhoto(String url, ImageView imageView)
        {
            PhotoToLoad p=new PhotoToLoad(url, imageView);
            executorService.submit(new PhotosLoader(p));
        }  
        private Bitmap getBitmap(String url)
        {
            File f=fileCache.getFile(url);
            /**
             * 先从文件缓存中查找是否有
             */
            //from SD cache
            Bitmap b = decodeFile(f);
            if(b!=null)
                return b;
            /**
             *  最后从指定的url中下载图片
             */
            //from web
            try {
                Bitmap bitmap=null;
                URL imageUrl = new URL(url);
                HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
                conn.setConnectTimeout(30000);
                conn.setReadTimeout(30000);
                conn.setInstanceFollowRedirects(true);
                InputStream is=conn.getInputStream();
                OutputStream os = new FileOutputStream(f);
                Utils.CopyStream(is, os);
                os.close();
                conn.disconnect();
                bitmap = decodeFile(f);
                return bitmap;
            } catch (Throwable ex){
               ex.printStackTrace();
               if(ex instanceof OutOfMemoryError)
                   memoryCache.clear();
               return null;
            }
        }
        /**
         *  decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的
         */
        //decodes image and scales it to reduce memory consumption
        private Bitmap decodeFile(File f){
            try {
                //decode image size
                BitmapFactory.Options o = new BitmapFactory.Options();
                o.inJustDecodeBounds = true;
                FileInputStream stream1=new FileInputStream(f);
                BitmapFactory.decodeStream(stream1,null,o);
                stream1.close();  
                //Find the correct scale value. It should be the power of 2.
                final int REQUIRED_SIZE=70;
                int width_tmp=o.outWidth, height_tmp=o.outHeight;
                int scale=1;
                while(true){
                    if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                        break;
                    width_tmp/=2;
                    height_tmp/=2;
                    scale*=2;
                }  
                //decode with inSampleSize
                BitmapFactory.Options o2 = new BitmapFactory.Options();
                o2.inSampleSize=scale;
                FileInputStream stream2=new FileInputStream(f);
                Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2);
                stream2.close();
                return bitmap;
            } catch (FileNotFoundException e) {
            }
            catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }  
        //Task for the queue
        private class PhotoToLoad
        {
            public String url;
            public ImageView imageView;
            public PhotoToLoad(String u, ImageView i){
                url=u;
                imageView=i;
            }
        }  
        class PhotosLoader implements Runnable {
            PhotoToLoad photoToLoad;
            PhotosLoader(PhotoToLoad photoToLoad){
                this.photoToLoad=photoToLoad;
            }  
            @Override
            public void run() {
                try{
                    if(imageViewReused(photoToLoad))
                        return;
                    Bitmap bmp=getBitmap(photoToLoad.url);
                    memoryCache.put(photoToLoad.url, bmp);
                    if(imageViewReused(photoToLoad))
                        return;
                    BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
                    handler.post(bd);
                }catch(Throwable th){
                    th.printStackTrace();
                }
            }
        }
        /**
         * 防止图片错位
         * @param photoToLoad
         * @return
         */
        boolean imageViewReused(PhotoToLoad photoToLoad){
            String tag=imageViews.get(photoToLoad.imageView);
            if(tag==null || !tag.equals(photoToLoad.url))
                return true;
            return false;
        }
        /**
         *  用于在UI线程中更新界面
         *
         */
        //Used to display bitmap in the UI thread
        class BitmapDisplayer implements Runnable
        {
            Bitmap bitmap;
            PhotoToLoad photoToLoad;
            public BitmapDisplayer(Bitmap b, PhotoToLoad p){
                bitmap=b;
                photoToLoad=p;
                }
            public void run()
            {
                if(imageViewReused(photoToLoad))
                    return;
                if(bitmap!=null)
                    photoToLoad.imageView.setImageBitmap(bitmap);
                else
                    photoToLoad.imageView.setImageResource(stub_id);
            }
        }  
        public void clearCache() {
            memoryCache.clear();
            fileCache.clear();
        }  
    }  </span>
由于篇幅的原因,FileCache和MenoryCache这两个缓存类,我就不贴大量代码了,全部都在我提供的demo示例代码中,你可以仔细的去研究一下,并有详细的注释,大家下载就可以用在自己的项目中了。
Listview优化的demo地址:http://download.csdn.net/detail/finddreams/8141689
原文:http://blog.csdn.net/finddreams/article/details/40977451
关注公众号,分享干货,讨论技术
详解ListView加载网络图片的优化的更多相关文章
- 详解ListView加载网络图片的优化,让你轻松掌握!
		详解ListView加载网络图片的优化,让你轻松掌握! 写博客辛苦了,转载的朋友请标明出处哦,finddreams(http://blog.csdn.net/finddreams/article/de ... 
- 一步一步实现listview加载的性能优化
		listview加载的核心是其adapter,本文针对listview加载的性能优化就是对adpter的优化,总共分四个层次: 0.最原始的加载 1.利用convertView 2.利用ViewHol ... 
- listview加载网络图片
		ListView加载网络数据和图片 2013-09-25 00:08:10| 分类: 默认分类 | 标签:android |举报|字号 订阅 如,从服务器端获得商品名称.价格.简介和图片 ... 
- Google推荐——Glide使用详解(图片加载框架)
		零.前言 本文所使用的Glide版本为3.7.0 一.简介 Glide,一个被google所推荐的图片加载库,作者是bumptech.这个库被广泛运用在google的开源项目中,包括2014年的goo ... 
- [android]完美的解决方案ListView加载网络图片反弹问题
		为什么 先说为什么有照片反弹. 使用convertView对ListView的每一个item优化,item的复用能够有效减少内存的占用.使ListView滑动更为流畅. 但会带来一个问题,当最顶部的i ... 
- linux modprobe命令参数及用法详解--linux加载模块命令
		转:http://www.linuxso.com/command/modprobe.html modprobe(module probe) 功能说明:自动处理可载入模块. 语 法:modprobe [ ... 
- 详解Class加载过程
		1.Class文件内容格式 2.一个class文件是被加载到内存的过程是怎样的? loading 把一个class文件装到内存里,class文件是一个二进制,一个个的字节 linking Verifi ... 
- wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)
		wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之L ... 
- [WP8.1UI控件编程]Windows Phone大数据量网络图片列表的异步加载和内存优化
		11.2.4 大数据量网络图片列表的异步加载和内存优化 虚拟化技术可以让Windows Phone上的大数据量列表不必担心会一次性加载所有的数据,保证了UI的流程性.对于虚拟化的技术,我们不仅仅只是依 ... 
随机推荐
- STM32单片机是如何启动的?
			STM32单片机是如何启动的? STM32中的内存 STM32中的内存包含两块主要区域:flash memory(只读).static ram memory(SRAM,读写).其中,flash mem ... 
- java  DTO 转 POJO
			如果这两个类的要转化的属性其属性名不一样的话,那只能用get和set方法赋值 如果你的两个类要转化的属性名都一样,那可以用org.springframework.beans.BeanUtils这个类来 ... 
- ThinkPHP - 1 - 本地部署
			ThinkPHP ThinkPHP是一个快速.简单的基于MVC和面向对象的轻量级PHP开发框架,遵循Apache2开源协议发布,从诞生以来一直秉承简洁实用的设计原则,在保持出色的性能和至简的代码的同时 ... 
- HADOOP docker(四):安装hive
			1.hive简介2.安装hive2.1 环境准备2.1.1 下载安装包2.1.2 设置hive用户的环境变量2.1.3 hive服务端配置文件2.1.4 hive客户端配置文件2.1.4 分发hive ... 
- [leetcode-738-Monotone Increasing Digits]
			Given a non-negative integer N, find the largest number that is less than or equal to N with monoton ... 
- 推荐形参使用常量引用:void func(const T &);
			一.声明为const的原因: 把函数不会改变的形参定义成普通的引用会带给函数的调用者一种误导,即函数可以修改它的实参的值: 限制函数所能接受的实参类型,如不能把const对象.字面值或者需要类型转换的 ... 
- 学霸系统PipeLine功能规格说明书
			学霸系统PipeLine功能规格说明书共分为以下三部分: 1.产品面向用户群体 2.用户使用说明 3.产品功能具体实现 1.产品面向用户群体 我们这组的项目并不是传统意义上能发布并进行展示的项目,因此 ... 
- ueditor百度编辑器的赋值方法
			示例: http://ueditor.baidu.com/website/onlinedemo.html 引用代码: window.UMEDITOR_HOME_URL = $CONFIG['domai ... 
- listBox和pictureBox的使用
			重要属性:pictureBox中SizeMode可以更改图像显示的尺寸大小. using System; using System.Collections.Generic; using System. ... 
- QT分析之调试跟踪系统
			原文地址:http://blog.163.com/net_worm/blog/static/127702419201002004518944/ 在我们前面的分析中,经常看到qWarning()和qDe ... 
