免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2717 | 回复: 1
打印 上一主题 下一主题

又优化了一下 Android ListView 异步加载图片 [复制链接]

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




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



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



欢迎大家拍砖讨论

91db09e0-0462-31c8-82c5-cc9f9003de2f.png (49.19 KB, 下载次数: 13)

91db09e0-0462-31c8-82c5-cc9f9003de2f.png

论坛徽章:
0
2 [报告]
发表于 2011-12-23 22:15 |只看该作者
谢谢分享  希望于楼主多多交流
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP