当前位置: 代码迷 >> Android >> 又优化了一下子 Android ListView 异步加载图片
  详细解决方案

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

热度:86   发布时间:2016-05-01 19:31:41.0
又优化了一下 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


部分代码如下:
@Overridepublic 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			}};
?
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卡缓存)

?

用真机测试了一下,感觉无比流畅(测试机是U8500非常垃圾的一种低端机)

?

欢迎大家拍砖讨论


1 楼 yangqinjiang 2011-12-10  
不错啊不错啊不错啊不错啊
2 楼 huangzubin13 2012-02-14  
体验了一下,不错, 



不是标题党
3 楼 jdpxiaoming 2012-05-25  
不错 ,楼主辛苦了 我就 收下了
4 楼 jdpxiaoming 2012-05-25  
各有好处吧,你的 体验好点 但是 以前的 做法 节省时间 并发 任务 横分利用 cpu.!
5 楼 xyleon 2012-06-03  
不错,创意很好
6 楼 zxxcos 2012-06-04  
不错啊不错啊不错啊不错啊
  相关解决方案