• 又优化了一下 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. };  
     
    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. }  

    为了让大家更好的理解,我添加了源代码例子,还特地美化了一下UI


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

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

    欢迎大家拍砖讨论

     
  • 相关阅读:
    字符串中的不可见字符应该如何清除?
    字符/字段数据的合并
    分割字符串的应用
    几种分割字符串实现方法的比较
    linux的一些文件基本命令
    centos7安装es6.4.0
    Sql 语句中 IN 和 EXISTS 的区别及应用
    Springboot通过redisTemplate实现发布订阅
    代理模式
    单例模式的多种实现方法
  • 原文地址:https://www.cnblogs.com/xgjblog/p/4512695.html
Copyright © 2020-2023  润新知