网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决。下面提出一些优化:

1、采用线程池

2、内存缓存+文件缓存

3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4

4、对下载的图片进行按比例缩放,以减少内存的消耗

具体的代码里面说明。先放上内存缓存类的代码MemoryCache.java:


[java] ​​ view plain​​ ​​copy​​



  1. public class
  2.   
  3. private static final String TAG = "MemoryCache";  
  4. // 放入缓存时是个同步操作
  5. // LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU
  6. // 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率
  7. private
  8. new LinkedHashMap<String, Bitmap>(10, 1.5f, true));  
  9. // 缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存
  10. private long size = 0;// current allocated size
  11. // 缓存只能占用的最大堆内存
  12. private long limit = 1000000;// max memory in bytes
  13.   
  14. public
  15. // use 25% of available heap size
  16. 4);  
  17.     }  
  18.   
  19. public void setLimit(long
  20.         limit = new_limit;  
  21. "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");  
  22.     }  
  23.   
  24. public
  25. try
  26. if
  27. return null;  
  28. return
  29. catch
  30. return null;  
  31.         }  
  32.     }  
  33.   
  34. public void
  35. try
  36. if
  37.                 size -= getSizeInBytes(cache.get(id));  
  38.             cache.put(id, bitmap);  
  39.             size += getSizeInBytes(bitmap);  
  40.             checkSize();  
  41. catch
  42.             th.printStackTrace();  
  43.         }  
  44.     }  
  45.   
  46. /**
  47.      * 严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存
  48.      * 
  49.      */
  50. private void
  51. "cache size=" + size + " length="
  52. if
  53. // 先遍历最近最少使用的元素
  54.             Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();  
  55. while
  56.                 Entry<String, Bitmap> entry = iter.next();  
  57.                 size -= getSizeInBytes(entry.getValue());  
  58.                 iter.remove();  
  59. if
  60. break;  
  61.             }  
  62. "Clean cache. New size "
  63.         }  
  64.     }  
  65.   
  66. public void
  67.         cache.clear();  
  68.     }  
  69.   
  70. /**
  71.      * 图片占用的内存
  72.      * 
  73.      * @param bitmap
  74.      * @return
  75.      */
  76. long
  77. if (bitmap == null)  
  78. return 0;  
  79. return
  80.     }  
  81. }  


也可以使用SoftReference,代码会简单很多,但是我推荐上面的方法。


[java] ​​ view plain​​ ​​copy​​



  1. public class
  2.       
  3. private
  4. new
  5.   
  6. public
  7. if
  8. return null;  
  9.         SoftReference<Bitmap> ref = cache.get(id);  
  10. return
  11.     }  
  12.   
  13. public void
  14. new
  15.     }  
  16.   
  17. public void
  18.         cache.clear();  
  19.     }  
  20.   
  21. }  

下面是文件缓存类的代码FileCache.java:

[java] ​​ view plain​​ ​​copy​​



  1. public class
  2.   
  3. private
  4.   
  5. public
  6. // 如果有SD卡则在SD卡中建一个LazyList的目录存放缓存的图片
  7. // 没有SD卡就放在系统的缓存目录中
  8. if
  9.                 android.os.Environment.MEDIA_MOUNTED))  
  10. new
  11.                     android.os.Environment.getExternalStorageDirectory(),  
  12. "LazyList");  
  13. else
  14.             cacheDir = context.getCacheDir();  
  15. if
  16.             cacheDir.mkdirs();  
  17.     }  
  18.   
  19. public
  20. // 将url的hashCode作为缓存的文件名
  21.         String filename = String.valueOf(url.hashCode());  
  22. // Another possible solution
  23. // String filename = URLEncoder.encode(url);
  24. new
  25. return
  26.   
  27.     }  
  28.   
  29. public void
  30.         File[] files = cacheDir.listFiles();  
  31. if (files == null)  
  32. return;  
  33. for
  34.             f.delete();  
  35.     }  
  36.   
  37. }  

最后最重要的加载图片的类,ImageLoader.java:

[java] ​​ view plain​​ ​​copy​​



  1. public class
  2.   
  3. new
  4.     FileCache fileCache;  
  5. private
  6. new
  7. // 线程池
  8.     ExecutorService executorService;  
  9.   
  10. public
  11. new
  12. 5);  
  13.     }  
  14.   
  15. // 当进入listview时默认的图片,可换成你自己的默认图片
  16. final int
  17.   
  18. // 最主要的方法
  19. public void
  20.         imageViews.put(imageView, url);  
  21. // 先从内存缓存中查找
  22.   
  23.         Bitmap bitmap = memoryCache.get(url);  
  24. if (bitmap != null)  
  25.             imageView.setImageBitmap(bitmap);  
  26. else
  27. // 若没有的话则开启新线程加载图片
  28.             queuePhoto(url, imageView);  
  29.             imageView.setImageResource(stub_id);  
  30.         }  
  31.     }  
  32.   
  33. private void
  34. new
  35. new
  36.     }  
  37.   
  38. private
  39.         File f = fileCache.getFile(url);  
  40.   
  41. // 先从文件缓存中查找是否有
  42.         Bitmap b = decodeFile(f);  
  43. if (b != null)  
  44. return
  45.   
  46. // 最后从指定的url中下载图片
  47. try
  48. null;  
  49. new
  50.             HttpURLConnection conn = (HttpURLConnection) imageUrl  
  51.                     .openConnection();  
  52. 30000);  
  53. 30000);  
  54. true);  
  55.             InputStream is = conn.getInputStream();  
  56. new
  57.             CopyStream(is, os);  
  58.             os.close();  
  59.             bitmap = decodeFile(f);  
  60. return
  61. catch
  62.             ex.printStackTrace();  
  63. return null;  
  64.         }  
  65.     }  
  66.   
  67. // decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的
  68. private
  69. try
  70. // decode image size
  71. new
  72. true;  
  73. new FileInputStream(f), null, o);  
  74.   
  75. // Find the correct scale value. It should be the power of 2.
  76. final int REQUIRED_SIZE = 70;  
  77. int
  78. int scale = 1;  
  79. while (true) {  
  80. if (width_tmp / 2
  81. 2
  82. break;  
  83. 2;  
  84. 2;  
  85. 2;  
  86.             }  
  87.   
  88. // decode with inSampleSize
  89. new
  90.             o2.inSampleSize = scale;  
  91. return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);  
  92. catch
  93.         }  
  94. return null;  
  95.     }  
  96.   
  97. // Task for the queue
  98. private class
  99. public
  100. public
  101.   
  102. public
  103.             url = u;  
  104.             imageView = i;  
  105.         }  
  106.     }  
  107.   
  108. class PhotosLoader implements
  109.         PhotoToLoad photoToLoad;  
  110.   
  111.         PhotosLoader(PhotoToLoad photoToLoad) {  
  112. this.photoToLoad = photoToLoad;  
  113.         }  
  114.   
  115. @Override
  116. public void
  117. if
  118. return;  
  119.             Bitmap bmp = getBitmap(photoToLoad.url);  
  120.             memoryCache.put(photoToLoad.url, bmp);  
  121. if
  122. return;  
  123. new
  124. // 更新的操作放在UI线程中
  125.             Activity a = (Activity) photoToLoad.imageView.getContext();  
  126.             a.runOnUiThread(bd);  
  127.         }  
  128.     }  
  129.   
  130. /**
  131.      * 防止图片错位
  132.      * 
  133.      * @param photoToLoad
  134.      * @return
  135.      */
  136. boolean
  137.         String tag = imageViews.get(photoToLoad.imageView);  
  138. if (tag == null
  139. return true;  
  140. return false;  
  141.     }  
  142.   
  143. // 用于在UI线程中更新界面
  144. class BitmapDisplayer implements
  145.         Bitmap bitmap;  
  146.         PhotoToLoad photoToLoad;  
  147.   
  148. public
  149.             bitmap = b;  
  150.             photoToLoad = p;  
  151.         }  
  152.   
  153. public void
  154. if
  155. return;  
  156. if (bitmap != null)  
  157.                 photoToLoad.imageView.setImageBitmap(bitmap);  
  158. else
  159.                 photoToLoad.imageView.setImageResource(stub_id);  
  160.         }  
  161.     }  
  162.   
  163. public void
  164.         memoryCache.clear();  
  165.         fileCache.clear();  
  166.     }  
  167.   
  168. public static void
  169. final int buffer_size = 1024;  
  170. try
  171. byte[] bytes = new byte[buffer_size];  
  172. for
  173. int count = is.read(bytes, 0, buffer_size);  
  174. if (count == -1)  
  175. break;  
  176. 0, count);  
  177.             }  
  178. catch
  179.         }  
  180.     }  
  181. }  

主要流程是先从内存缓存中查找,若没有再开线程,从文件缓存中查找都没有则从指定的url中查找,并对bitmap进行处理,最后通过下面方法对UI进行更新操作。

[java] ​​ view plain​​ ​​copy​​



  1. a.runOnUiThread(...);  


在你的程序中的基本用法:


[java] ​​ view plain​​ ​​copy​​



  1. ImageLoader imageLoader=new
  2. ...  
  3. imageLoader.DisplayImage(url, imageView);