优化 Android ListView 异步加载图片

先说说这篇文章的优点把,开启线程异步加载图片,然后刷新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

 
部分代码如下:
01 @Override
02 public View getView(int position, View convertView, ViewGroup parent) {
03     if(convertView == null){
04         convertView = mInflater.inflate(R.layout.book_item_adapter, null);
05     }
06     BookModel  model = mModels.get(position);
07     convertView.setTag(position);
08     ImageView iv = (ImageView) convertView.findViewById(R.id.sItemIcon);
09     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,imageLoadListener);
15     return  convertView;
16 }
17 SyncImageLoader.OnImageLoadListener imageLoadListener = new SyncImageLoader.OnImageLoadListener(){
18     @Override
19     public void onImageLoad(Integer t, Drawable drawable) {
20         //BookModel model = (BookModel) getItem(t);
21         View view = mListView.findViewWithTag(t);
22         if(view != null){
23             ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
24             iv.setBackgroundDrawable(drawable);
25         }
26     }
27     @Override
28     public void onError(Integer t) {
29         BookModel  model = (BookModel) getItem(t);
30         View view = mListView.findViewWithTag(model);
31         if(view != null){
32             ImageView iv = (ImageView) view.findViewById(R.id.sItemIcon);
33             iv.setBackgroundResource(R.drawable.rc_item_bg);
34         }
35     }
36                                                                                                                                                                      
37 };
38 public void loadImage(){
39     int start = mListView.getFirstVisiblePosition();
40     int end =mListView.getLastVisiblePosition();
41     if(end >= getCount()){
42         end = getCount() -1;
43     }
44     syncImageLoader.setLoadLimit(start, end);
45     syncImageLoader.unlock();
46 }
47 AbsListView.OnScrollListener onScrollListener = new AbsListView.OnScrollListener() {
48                                                                                                                                                                      
49     @Override
50     public void onScrollStateChanged(AbsListView view, int scrollState) {
51          switch (scrollState) {
52             case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
53                 DebugUtil.debug("SCROLL_STATE_FLING");
54                 syncImageLoader.lock();
55                 break;
56             case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
57                 DebugUtil.debug("SCROLL_STATE_IDLE");
58                 loadImage();
59                 //loadImage();
60                 break;
61             case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
62                 syncImageLoader.lock();
63                 break;
64             default:
65                 break;
66         }
67                                                                                                                                                                          
68     }
69                                                                                                                                                                      
70     @Override
71     public void onScroll(AbsListView view, int firstVisibleItem,
72             int visibleItemCount, int totalItemCount) {
73         // TODO Auto-generated method stub
74                                                                                                                                                                          
75     }
76 };

package cindy.android.test.synclistview;

Syncimageloader代码

001 import java.io.DataInputStream;
002 import java.io.File;
003 import java.io.FileInputStream;
004 import java.io.FileOutputStream;
005 import java.io.IOException;
006 import java.io.InputStream;
007 import java.lang.ref.SoftReference;
008 import java.net.URL;
009 import java.util.HashMap;
010 import android.graphics.drawable.Drawable;
011 import android.os.Environment;
012 import android.os.Handler;
013 public class SyncImageLoader {
014     private Object lock = new Object();
015                                                                                                                                          
016     private boolean mAllowLoad = true;
017                                                                                                                                          
018     private boolean firstLoad = true;
019                                                                                                                                          
020     private int mStartLoadLimit = 0;
021                                                                                                                                          
022     private int mStopLoadLimit = 0;
023                                                                                                                                          
024     final Handler handler = new Handler();
025                                                                                                                                          
026     private HashMap<String, SoftReference<Drawable>> imageCache = new HashMap<String, SoftReference<Drawable>>();
027                                                                                                                                          
028     public interface OnImageLoadListener {
029         public void onImageLoad(Integer t, Drawable drawable);
030         public void onError(Integer t);
031     }
032                                                                                                                                          
033     public void setLoadLimit(int startLoadLimit,int stopLoadLimit){
034         if(startLoadLimit > stopLoadLimit){
035             return;
036         }
037         mStartLoadLimit = startLoadLimit;
038         mStopLoadLimit = stopLoadLimit;
039     }
040                                                                                                                                          
041     public void restore(){
042         mAllowLoad = true;
043         firstLoad = true;
044     }
045                                                                                                                                              
046     public void lock(){
047         mAllowLoad = false;
048         firstLoad = false;
049     }
050                                                                                                                                          
051     public void unlock(){
052         mAllowLoad = true;
053         synchronized (lock) {
054             lock.notifyAll();
055         }
056     }
057     public void loadImage(Integer t, String imageUrl,
058             OnImageLoadListener listener) {
059         final OnImageLoadListener mListener = listener;
060         final String mImageUrl = imageUrl;
061         final Integer mt = t;
062                                                                                                                                              
063         new Thread(new Runnable() {
064             @Override
065             public void run() {
066                 if(!mAllowLoad){
067                     DebugUtil.debug("prepare to load");
068                     synchronized (lock) {
069                         try {
070                             lock.wait();
071                         } catch (InterruptedException e) {
072                             // TODO Auto-generated catch  block
073                             e.printStackTrace();
074                         }
075                     }
076                 }
077                                                                                                                                                      
078                 if(mAllowLoad && firstLoad){
079                     loadImage(mImageUrl, mt, mListener);
080                 }
081                                                                                                                                                      
082                 if(mAllowLoad && mt <= mStopLoadLimit && mt >= mStartLoadLimit){
083                     loadImage(mImageUrl, mt, mListener);
084                 }
085             }
086         }).start();
087     }
088                                                                                                                                          
089     private void loadImage(final String mImageUrl,final Integer mt,final OnImageLoadListener mListener){
090                                                                                                                                              
091         if (imageCache.containsKey(mImageUrl)) {
092             SoftReference<Drawable> softReference = imageCache.get(mImageUrl);
093             final Drawable d = softReference.get();
094             if (d != null) {
095                 handler.post(new Runnable() {
096                     @Override
097                     public void run() {
098                         if(mAllowLoad){
099                             mListener.onImageLoad(mt, d);
100                         }
101                     }
102                 });
103                 return;
104             }
105         }
106         try {
107             final Drawable d = loadImageFromUrl(mImageUrl);
108             if(d != null){
109                 imageCache.put(mImageUrl, new SoftReference<Drawable>(d));
110             }
111             handler.post(new Runnable() {
112                 @Override
113                 public void run() {
114                     if(mAllowLoad){
115                         mListener.onImageLoad(mt, d);
116                     }
117                 }
118             });
119         } catch (IOException e) {
120             handler.post(new Runnable() {
121                 @Override
122                 public void run() {
123                     mListener.onError(mt);
124                 }
125             });
126             e.printStackTrace();
127         }
128     }
129     public static Drawable loadImageFromUrl(String url) throws IOException {
130         DebugUtil.debug(url);
131         if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
132             File f = new File(Environment.getExternalStorageDirectory()+"/TestSyncListView/"+MD5.getMD5(url));
133             if(f.exists()){
134                 FileInputStream fis = new FileInputStream(f);
135                 Drawable d = Drawable.createFromStream(fis, "src");
136                 return d;
137             }
138             URL m = new URL(url);
139             InputStream i = (InputStream) m.getContent();
140             DataInputStream in = new DataInputStream(i);
141             FileOutputStream out = new FileOutputStream(f);
142             byte[] buffer = new byte[1024];
143             int   byteread=0;
144             while ((byteread = in.read(buffer)) != -1) {
145                 out.write(buffer, 0, byteread);
146             }
147             in.close();
148             out.close();
149             Drawable d = Drawable.createFromStream(i, "src");
150             return loadImageFromUrl(url);
151         }else{
152             URL m = new URL(url);
153             InputStream i = (InputStream) m.getContent();
154             Drawable d = Drawable.createFromStream(i, "src");
155             return d;
156         }
157                                                                                                                                              
158     }
159 }

为了让大家更好的理解,我添加了源代码例子,还特地美化了一下UI
优化 Android ListView 异步加载图片_第1张图片
很多同学说在这里一直new Thread可能会造成资源浪费的问题,针对这个问题我后来又做了优化:

其实改动不大,就是把之前的new Thread改成了 Handler Looper Thread的模式,这样在第一次滑动的时候就进入了wait状态,又因为handler里面的runnable是队列执行的,所以handler一直在添加的runnable也在等待,这样就避免了多次new thread的问题,从头到尾就只有一个thread,别的不多说,看修改后的代码。

源码我就不上传了,就添加了一个类,修改了一个类:

01 package cindy.android.util;
02 import android.os.Handler;
03 import android.os.Looper;
04 import android.os.Message;
05 public class RunInOtherThread {
06     private static final String LOG_TAG = "RunInOtherThread";
07                                                                                                      
08     private LooperThread localThread = new LooperThread();
09                                                                                                      
10     private boolean isRunning = true;
11     public Handler getHandler(){
12         return localThread.getHandler();
13     }
14                                                                                                      
15     private class LooperThread extends Thread {
16         private Handler mHandler;
17         public void run() {
18             Looper.prepare();
19             mHandler = new Handler() {
20                 public void handleMessage(Message msg) {
21                     onReceiveMessage(msg.what);
22                 }
23             };
24             Looper.loop();
25         }
26                                                                                                          
27         Handler getHandler(){
28             return mHandler;
29         }
30                                                                                                     
31     }
32                                                                                                      
33     public void start(){
34         localThread.start();
35     }
36                                                                                                      
37     public void quit(){
38         localThread.getHandler().getLooper().quit();
39     }
40                                                                                                      
41     public void sendMessage(int what){
42         getHandler().sendEmptyMessage(what);
43     }
44                                                                                                      

你可能感兴趣的:(android,ListView,异步加载图片)