zoukankan      html  css  js  c++  java
  • 优化 Android ListView 异步加载图片

    写这篇文章并不是教大家怎么样用listview异步加载图片,因为这样的文章在网上已经有很多了,比如这位仁兄写的就很好:

    http://www.iteye.com/topic/685986

    我也是因为看了这篇文章而受到了启发。

    先说说这篇文章的优点把,开启线程异步加载图片,然后刷新UI显示图片,而且通过弱引用缓存网络加载的图片,节省了再次连接网络的开销。

    这样做无疑是非常可取的方法,但是加载图片时仍然会感觉到轻微的卡屏现象,特别是listview里的item在进行快速滑动的时候。

    我找了一下原因,可能是在listview快速滑动屏幕的时候划过的item太多 而且每次调用getView方法后就会异步的在过去某个时间内用handler刷新一下UI,

    如果在同一时间调用handler刷新UI次数多了就会造成这样的卡屏现象。

    后来又一想,其实我们完全没有必要在listview正在滑动的时候去后台加载图片(不管这是图片是在缓存里还是在网络上),这样无疑造成了很大的资源浪费。

    我们只需要在listview滑动停止之后再去加载listview里面显示的几个item里面的图片就好了。

    根据以上想法,我做了一些设计改造:

    1.在adapter 的 getview方法里面启动加载图片的thread,如果listview在滑动则wait

    2.监听listview滑动停止事件,获得listview显示的item的最上面和最下面的序号,并唤醒所有加载图片的thread,判断加载图片的序号是否是在范围内,如果是则继续加载,如果不是则结束thread


    部分代码如下:
    Java代码  收藏代码
    1. @Override  
    2. public View getView(int position, View convertView, ViewGroup parent) {  
    3.     if(convertView == null){  
    4.         convertView = mInflater.inflate(R.layout.book_item_adapter, null);  
    5.     }  
    6.     BookModel model = mModels.get(position);  
    7.     convertView.setTag(position);  
    8.     ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon);  
    9.     TextView sItemTitle =  (TextView) convertView.findViewById(R.id.sItemTitle);  
    10.     TextView sItemInfo =  (TextView) convertView.findViewById(R.id.sItemInfo);  
    11.     sItemTitle.setText(model.book_name);  
    12.     sItemInfo.setText(model.out_book_url);  
    13.     iv.setBackgroundResource(R.drawable.rc_item_bg);  
    14.     syncImageLoader.loadImage(position,model.out_book_pic,imageLoadListener);  
    15.     return  convertView;  
    16. }  
    17.   
    18. SyncImageLoader.OnImageLoadListener imageLoadListener = new SyncImageLoader.OnImageLoadListener(){  
    19.   
    20.     @Override  
    21.     public void onImageLoad(Integer t, Drawable drawable) {  
    22.         //BookModel model = (BookModel) getItem(t);  
    23.         View view = mListView.findViewWithTag(t);  
    24.         if(view != null){  
    25.             ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);  
    26.             iv.setBackgroundDrawable(drawable);  
    27.         }  
    28.     }  
    29.     @Override  
    30.     public void onError(Integer t) {  
    31.         BookModel model = (BookModel) getItem(t);  
    32.         View view = mListView.findViewWithTag(model);  
    33.         if(view != null){  
    34.             ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);  
    35.             iv.setBackgroundResource(R.drawable.rc_item_bg);  
    36.         }  
    37.     }  
    38.       
    39. };  
    40.   
    41. public void loadImage(){  
    42.     int start = mListView.getFirstVisiblePosition();  
    43.     int end =mListView.getLastVisiblePosition();  
    44.     if(end >= getCount()){  
    45.         end = getCount() -1;  
    46.     }  
    47.     syncImageLoader.setLoadLimit(start, end);  
    48.     syncImageLoader.unlock();  
    49. }  
    50.   
    51. AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {  
    52.       
    53.     @Override  
    54.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
    55.         switch (scrollState) {  
    56.             case AbsListView.OnScrollListener.SCROLL_STATE_FLING:  
    57.                 DebugUtil.debug("SCROLL_STATE_FLING");  
    58.                 syncImageLoader.lock();  
    59.                 break;  
    60.             case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:  
    61.                 DebugUtil.debug("SCROLL_STATE_IDLE");  
    62.                 loadImage();  
    63.                 //loadImage();  
    64.                 break;  
    65.             case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:  
    66.                 syncImageLoader.lock();  
    67.                 break;  
    68.   
    69.             default:  
    70.                 break;  
    71.         }  
    72.           
    73.     }  
    74.       
    75.     @Override  
    76.     public void onScroll(AbsListView view, int firstVisibleItem,  
    77.             int visibleItemCount, int totalItemCount) {  
    78.         // TODO Auto-generated method stub  
    79.           
    80.     }  
    81. };  
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
    	if(convertView == null){
    		convertView = mInflater.inflate(R.layout.book_item_adapter, null);
    	}
    	BookModel model = mModels.get(position);
    	convertView.setTag(position);
    	ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon);
    	TextView sItemTitle =  (TextView) convertView.findViewById(R.id.sItemTitle);
    	TextView sItemInfo =  (TextView) convertView.findViewById(R.id.sItemInfo);
    	sItemTitle.setText(model.book_name);
    	sItemInfo.setText(model.out_book_url);
    	iv.setBackgroundResource(R.drawable.rc_item_bg);
    	syncImageLoader.loadImage(position,model.out_book_pic,imageLoadListener);
    	return  convertView;
    }
    
    SyncImageLoader.OnImageLoadListener imageLoadListener = new SyncImageLoader.OnImageLoadListener(){
    
    	@Override
    	public void onImageLoad(Integer t, Drawable drawable) {
    		//BookModel model = (BookModel) getItem(t);
    		View view = mListView.findViewWithTag(t);
    		if(view != null){
    			ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
    			iv.setBackgroundDrawable(drawable);
    		}
    	}
    	@Override
    	public void onError(Integer t) {
    		BookModel model = (BookModel) getItem(t);
    		View view = mListView.findViewWithTag(model);
    		if(view != null){
    			ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
    			iv.setBackgroundResource(R.drawable.rc_item_bg);
    		}
    	}
    	
    };
    
    public void loadImage(){
    	int start = mListView.getFirstVisiblePosition();
    	int end =mListView.getLastVisiblePosition();
    	if(end >= getCount()){
    		end = getCount() -1;
    	}
    	syncImageLoader.setLoadLimit(start, end);
    	syncImageLoader.unlock();
    }
    
    AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
    	
    	@Override
    	public void onScrollStateChanged(AbsListView view, int scrollState) {
    		switch (scrollState) {
    			case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
    				DebugUtil.debug("SCROLL_STATE_FLING");
    				syncImageLoader.lock();
    				break;
    			case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
    				DebugUtil.debug("SCROLL_STATE_IDLE");
    				loadImage();
    				//loadImage();
    				break;
    			case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
    				syncImageLoader.lock();
    				break;
    
    			default:
    				break;
    		}
    		
    	}
    	
    	@Override
    	public void onScroll(AbsListView view, int firstVisibleItem,
    			int visibleItemCount, int totalItemCount) {
    		// TODO Auto-generated method stub
    		
    	}
    };
     package cindy.android.test.synclistview;
    Syncimageloader代码  收藏代码
    1. import java.io.DataInputStream;  
    2. import java.io.File;  
    3. import java.io.FileInputStream;  
    4. import java.io.FileOutputStream;  
    5. import java.io.IOException;  
    6. import java.io.InputStream;  
    7. import java.lang.ref.SoftReference;  
    8. import java.net.URL;  
    9. import java.util.HashMap;  
    10.   
    11. import android.graphics.drawable.Drawable;  
    12. import android.os.Environment;  
    13. import android.os.Handler;  
    14.   
    15. public class SyncImageLoader {  
    16.   
    17.     private Object lock = new Object();  
    18.       
    19.     private boolean mAllowLoad = true;  
    20.       
    21.     private boolean firstLoad = true;  
    22.       
    23.     private int mStartLoadLimit = 0;  
    24.       
    25.     private int mStopLoadLimit = 0;  
    26.       
    27.     final Handler handler = new Handler();  
    28.       
    29.     private HashMap<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();     
    30.       
    31.     public interface OnImageLoadListener {  
    32.         public void onImageLoad(Integer t, Drawable drawable);  
    33.         public void onError(Integer t);  
    34.     }  
    35.       
    36.     public void setLoadLimit(int startLoadLimit,int stopLoadLimit){  
    37.         if(startLoadLimit > stopLoadLimit){  
    38.             return;  
    39.         }  
    40.         mStartLoadLimit = startLoadLimit;  
    41.         mStopLoadLimit = stopLoadLimit;  
    42.     }  
    43.       
    44.     public void restore(){  
    45.         mAllowLoad = true;  
    46.         firstLoad = true;  
    47.     }  
    48.           
    49.     public void lock(){  
    50.         mAllowLoad = false;  
    51.         firstLoad = false;  
    52.     }  
    53.       
    54.     public void unlock(){  
    55.         mAllowLoad = true;  
    56.         synchronized (lock) {  
    57.             lock.notifyAll();  
    58.         }  
    59.     }  
    60.   
    61.     public void loadImage(Integer t, String imageUrl,  
    62.             OnImageLoadListener listener) {  
    63.         final OnImageLoadListener mListener = listener;  
    64.         final String mImageUrl = imageUrl;  
    65.         final Integer mt = t;  
    66.           
    67.         new Thread(new Runnable() {  
    68.   
    69.             @Override  
    70.             public void run() {  
    71.                 if(!mAllowLoad){  
    72.                     DebugUtil.debug("prepare to load");  
    73.                     synchronized (lock) {  
    74.                         try {  
    75.                             lock.wait();  
    76.                         } catch (InterruptedException e) {  
    77.                             // TODO Auto-generated catch block  
    78.                             e.printStackTrace();  
    79.                         }  
    80.                     }  
    81.                 }  
    82.                   
    83.                 if(mAllowLoad && firstLoad){  
    84.                     loadImage(mImageUrl, mt, mListener);  
    85.                 }  
    86.                   
    87.                 if(mAllowLoad && mt <= mStopLoadLimit && mt >= mStartLoadLimit){  
    88.                     loadImage(mImageUrl, mt, mListener);  
    89.                 }  
    90.             }  
    91.   
    92.         }).start();  
    93.     }  
    94.       
    95.     private void loadImage(final String mImageUrl,final Integer mt,final OnImageLoadListener mListener){  
    96.           
    97.         if (imageCache.containsKey(mImageUrl)) {    
    98.             SoftReference<Drawable> softReference = imageCache.get(mImageUrl);    
    99.             final Drawable d = softReference.get();    
    100.             if (d != null) {    
    101.                 handler.post(new Runnable() {  
    102.                     @Override  
    103.                     public void run() {  
    104.                         if(mAllowLoad){  
    105.                             mListener.onImageLoad(mt, d);  
    106.                         }  
    107.                     }  
    108.                 });  
    109.                 return;    
    110.             }    
    111.         }    
    112.         try {  
    113.             final Drawable d = loadImageFromUrl(mImageUrl);  
    114.             if(d != null){  
    115.                 imageCache.put(mImageUrl, new SoftReference<Drawable>(d));  
    116.             }  
    117.             handler.post(new Runnable() {  
    118.                 @Override  
    119.                 public void run() {  
    120.                     if(mAllowLoad){  
    121.                         mListener.onImageLoad(mt, d);  
    122.                     }  
    123.                 }  
    124.             });  
    125.         } catch (IOException e) {  
    126.             handler.post(new Runnable() {  
    127.                 @Override  
    128.                 public void run() {  
    129.                     mListener.onError(mt);  
    130.                 }  
    131.             });  
    132.             e.printStackTrace();  
    133.         }  
    134.     }  
    135.   
    136.     public static Drawable loadImageFromUrl(String url) throws IOException {  
    137.         DebugUtil.debug(url);  
    138.         if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){  
    139.             File f = new File(Environment.getExternalStorageDirectory()+"/TestSyncListView/"+MD5.getMD5(url));  
    140.             if(f.exists()){  
    141.                 FileInputStream fis = new FileInputStream(f);  
    142.                 Drawable d = Drawable.createFromStream(fis, "src");  
    143.                 return d;  
    144.             }  
    145.             URL m = new URL(url);  
    146.             InputStream i = (InputStream) m.getContent();  
    147.             DataInputStream in = new DataInputStream(i);  
    148.             FileOutputStream out = new FileOutputStream(f);  
    149.             byte[] buffer = new byte[1024];  
    150.             int   byteread=0;  
    151.             while ((byteread = in.read(buffer)) != -1) {  
    152.                 out.write(buffer, 0, byteread);  
    153.             }  
    154.             in.close();  
    155.             out.close();  
    156.             Drawable d = Drawable.createFromStream(i, "src");  
    157.             return loadImageFromUrl(url);  
    158.         }else{  
    159.             URL m = new URL(url);  
    160.             InputStream i = (InputStream) m.getContent();  
    161.             Drawable d = Drawable.createFromStream(i, "src");  
    162.             return d;  
    163.         }  
    164.           
    165.     }  
    166. }  
    import java.io.DataInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.ref.SoftReference;
    import java.net.URL;
    import java.util.HashMap;
    
    import android.graphics.drawable.Drawable;
    import android.os.Environment;
    import android.os.Handler;
    
    public class SyncImageLoader {
    
    	private Object lock = new Object();
    	
    	private boolean mAllowLoad = true;
    	
    	private boolean firstLoad = true;
    	
    	private int mStartLoadLimit = 0;
    	
    	private int mStopLoadLimit = 0;
    	
    	final Handler handler = new Handler();
    	
    	private HashMap<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();   
    	
    	public interface OnImageLoadListener {
    		public void onImageLoad(Integer t, Drawable drawable);
    		public void onError(Integer t);
    	}
    	
    	public void setLoadLimit(int startLoadLimit,int stopLoadLimit){
    		if(startLoadLimit > stopLoadLimit){
    			return;
    		}
    		mStartLoadLimit = startLoadLimit;
    		mStopLoadLimit = stopLoadLimit;
    	}
    	
    	public void restore(){
    		mAllowLoad = true;
    		firstLoad = true;
    	}
    		
    	public void lock(){
    		mAllowLoad = false;
    		firstLoad = false;
    	}
    	
    	public void unlock(){
    		mAllowLoad = true;
    		synchronized (lock) {
    			lock.notifyAll();
    		}
    	}
    
    	public void loadImage(Integer t, String imageUrl,
    			OnImageLoadListener listener) {
    		final OnImageLoadListener mListener = listener;
    		final String mImageUrl = imageUrl;
    		final Integer mt = t;
    		
    		new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				if(!mAllowLoad){
    					DebugUtil.debug("prepare to load");
    					synchronized (lock) {
    						try {
    							lock.wait();
    						} catch (InterruptedException e) {
    							// TODO Auto-generated catch block
    							e.printStackTrace();
    						}
    					}
    				}
    				
    				if(mAllowLoad && firstLoad){
    					loadImage(mImageUrl, mt, mListener);
    				}
    				
    				if(mAllowLoad && mt <= mStopLoadLimit && mt >= mStartLoadLimit){
    					loadImage(mImageUrl, mt, mListener);
    				}
    			}
    
    		}).start();
    	}
    	
    	private void loadImage(final String mImageUrl,final Integer mt,final OnImageLoadListener mListener){
    		
    		if (imageCache.containsKey(mImageUrl)) {  
                SoftReference<Drawable> softReference = imageCache.get(mImageUrl);  
                final Drawable d = softReference.get();  
                if (d != null) {  
                	handler.post(new Runnable() {
        				@Override
        				public void run() {
        					if(mAllowLoad){
        						mListener.onImageLoad(mt, d);
        					}
        				}
        			});
                    return;  
                }  
            }  
    		try {
    			final Drawable d = loadImageFromUrl(mImageUrl);
    			if(d != null){
                    imageCache.put(mImageUrl, new SoftReference<Drawable>(d));
    			}
    			handler.post(new Runnable() {
    				@Override
    				public void run() {
    					if(mAllowLoad){
    						mListener.onImageLoad(mt, d);
    					}
    				}
    			});
    		} catch (IOException e) {
    			handler.post(new Runnable() {
    				@Override
    				public void run() {
    					mListener.onError(mt);
    				}
    			});
    			e.printStackTrace();
    		}
    	}
    
    	public static Drawable loadImageFromUrl(String url) throws IOException {
    		DebugUtil.debug(url);
    		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
    			File f = new File(Environment.getExternalStorageDirectory()+"/TestSyncListView/"+MD5.getMD5(url));
    			if(f.exists()){
    				FileInputStream fis = new FileInputStream(f);
    				Drawable d = Drawable.createFromStream(fis, "src");
    				return d;
    			}
    			URL m = new URL(url);
    			InputStream i = (InputStream) m.getContent();
    			DataInputStream in = new DataInputStream(i);
    			FileOutputStream out = new FileOutputStream(f);
    			byte[] buffer = new byte[1024];
    			int   byteread=0;
    			while ((byteread = in.read(buffer)) != -1) {
    				out.write(buffer, 0, byteread);
    			}
    			in.close();
    			out.close();
    			Drawable d = Drawable.createFromStream(i, "src");
    			return loadImageFromUrl(url);
    		}else{
    			URL m = new URL(url);
    			InputStream i = (InputStream) m.getContent();
    			Drawable d = Drawable.createFromStream(i, "src");
    			return d;
    		}
    		
    	}
    }
    
    为了让大家更好的理解,我添加了源代码例子,还特地美化了一下UI


    除了本身已有的弱引用缓存图片,我还添加了本地SD卡缓存图片(这两种缓存方法各有好处,如果图片经常变化建议内存缓存图片,如果是不经常修改的图片建议SD卡缓存)

    欢迎大家拍砖讨论


    • 大小: 49.2 KB
     
     
    转http://note.sdo.com/u/jesse_luo/n/sCPcz~jQFr6pnM2k0001nH
  • 相关阅读:
    js 几个特殊情况
    Oracle 常用操作
    SqlServer性能检测和优化工具使用详细
    Fiddler
    JMeter性能测试,完整入门篇
    asp.net缓存
    Quartz.net开源作业调度框架使用详解
    SQL 存储过程
    .net 分布式锁
    全国省市区数据库
  • 原文地址:https://www.cnblogs.com/jiezzy/p/2557422.html
Copyright © 2011-2022 走看看