异步加载图片的例子,网上也比较多,大部分用了HashMap<String, SoftReference<Drawable>> imageCache ,但是现在已经不再推荐使用这种方式了,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用和弱引用变得不再可靠。另外,Android 3.0 (API Level 11)中,图片的数据会存储在本地的内存当中,因而无法用一种可预见的方式将其释放,这就有潜在的风险造成应用程序的内存溢出并崩溃,所以我这里用得是LruCache来缓存图片,当存储Image的大小大于LruCache设定的值,系统自动释放内存,这个类是3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包(这里要注意咯)

为什么写这篇文章呢?

因为我之前做的项目中,也有异步加载图片,那时候用得是Thread去下载图片,每次下载图片都要new Thread去下载,而且还是并发去下载,每次都new 一个线程浪费内存,老板说服务器承受不起这么多的连接,叫我改成先获取一张图片之后再去获取下一张,这样子保存与服务器的连接为一个,服务器压力小了,然后楼主就想到线程池,线程池很好的帮我们管理并发的问题,并发的问题解决了,可是后面又出问题了,图片多了就出现OOM(OutOfMemory)异常,之后用了SoftReference,先用SoftReference中获取图片,SoftReference没有就开线程去下载,老板说你为什么不把图片在手机上做个缓存呢,于是我用了手机缓存,大概思路就是先从SoftReference中获取图片,如果SoftReference没有就去手机缓存中获取,手机缓存中没有就开启先从去下载,然后成功的解决了OOM的问题,前些天老板要我重构下代码,我也觉得之前写的代码耦合性太强,早就想改,然后之前看到guolin的Android照片墙应用实现,再多的图片也不怕崩溃的这篇文章,LruCache和滑动过程中取消下载任务,停下来的时候才去下载这2点比较好,值得我学习,然后我就将我的项目异步加载这一块改了下,发到这里做个记录吧,以后类似的异步加载图片直接拷贝代码,提交开发的效率

这篇文章做了哪些方面的优化

  1. 使用了线程池来管理下载任务
  2. 使用LruCache来缓存图片
  3. 使用手机来缓存图片
  4. GridView滑动的时候取消下载任务,静止的时候进行下载,GridView滑动更加的流畅
  5. 降低了代码的耦合性,结构更加的清晰,便于以后重用

接下来我们先来看看项目的结构

  • FileUtils 文件操作的工具类,提供保存图片,获取图片,判断图片是否存在,删除图片的一些方法,这个类比较简单
[java] view plaincopy
  1. package com.example.asyncimageloader;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import android.content.Context;
  6. import android.graphics.Bitmap;
  7. import android.graphics.Bitmap.CompressFormat;
  8. import android.graphics.BitmapFactory;
  9. import android.os.Environment;
  10. public class FileUtils {
  11. /**
  12. * sd卡的根目录
  13. */
  14. private static String mSdRootPath = Environment.getExternalStorageDirectory().getPath();
  15. /**
  16. * 手机的缓存根目录
  17. */
  18. private static String mDataRootPath = null;
  19. /**
  20. * 保存Image的目录名
  21. */
  22. private final static String FOLDER_NAME = "/AndroidImage";
  23. public FileUtils(Context context){
  24. mDataRootPath = context.getCacheDir().getPath();
  25. }
  26. /**
  27. * 获取储存Image的目录
  28. * @return
  29. */
  30. private String getStorageDirectory(){
  31. return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ?
  32. mSdRootPath + FOLDER_NAME : mDataRootPath + FOLDER_NAME;
  33. }
  34. /**
  35. * 保存Image的方法,有sd卡存储到sd卡,没有就存储到手机目录
  36. * @param fileName
  37. * @param bitmap
  38. * @throws IOException
  39. */
  40. public void savaBitmap(String fileName, Bitmap bitmap) throws IOException{
  41. if(bitmap == null){
  42. return;
  43. }
  44. String path = getStorageDirectory();
  45. File folderFile = new File(path);
  46. if(!folderFile.exists()){
  47. folderFile.mkdir();
  48. }
  49. File file = new File(path + File.separator + fileName);
  50. file.createNewFile();
  51. FileOutputStream fos = new FileOutputStream(file);
  52. bitmap.compress(CompressFormat.JPEG, 100, fos);
  53. fos.flush();
  54. fos.close();
  55. }
  56. /**
  57. * 从手机或者sd卡获取Bitmap
  58. * @param fileName
  59. * @return
  60. */
  61. public Bitmap getBitmap(String fileName){
  62. return BitmapFactory.decodeFile(getStorageDirectory() + File.separator + fileName);
  63. }
  64. /**
  65. * 判断文件是否存在
  66. * @param fileName
  67. * @return
  68. */
  69. public boolean isFileExists(String fileName){
  70. return new File(getStorageDirectory() + File.separator + fileName).exists();
  71. }
  72. /**
  73. * 获取文件的大小
  74. * @param fileName
  75. * @return
  76. */
  77. public long getFileSize(String fileName) {
  78. return new File(getStorageDirectory() + File.separator + fileName).length();
  79. }
  80. /**
  81. * 删除SD卡或者手机的缓存图片和目录
  82. */
  83. public void deleteFile() {
  84. File dirFile = new File(getStorageDirectory());
  85. if(! dirFile.exists()){
  86. return;
  87. }
  88. if (dirFile.isDirectory()) {
  89. String[] children = dirFile.list();
  90. for (int i = 0; i < children.length; i++) {
  91. new File(dirFile, children[i]).delete();
  92. }
  93. }
  94. dirFile.delete();
  95. }
  96. }<span style="font-family:Times New Roman;font-size:14px;">
  97. </span>
  • Images类,提供图片资源的类,来自Android文档中BitmapFun中http://developer.android.com/training/displaying-bitmaps/index.html,里面一个静态的图片Url数组,这里面比较坑爹,因为是图片是google官网提供的,有时候会下载超时
[java] view plaincopy
  1. package com.example.asyncimageloader;
  2. public class Images {
  3. public final static String[] imageThumbUrls = new String[] {
  4. "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s160-c/A%252520Photographer.jpg",
  5. "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s160-c/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
  6. "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s160-c/Another%252520Rockaway%252520Sunset.jpg",
  7. "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s160-c/Antelope%252520Butte.jpg",
  8. "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s160-c/Antelope%252520Hallway.jpg",
  9. "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s160-c/Antelope%252520Walls.jpg",
  10. "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s160-c/Apre%2525CC%252580s%252520la%252520Pluie.jpg",
  11. "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s160-c/Backlit%252520Cloud.jpg",
  12. "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s160-c/Bee%252520and%252520Flower.jpg",
  13. "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s160-c/Bonzai%252520Rock%252520Sunset.jpg",
  14. "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s160-c/Caterpillar.jpg",
  15. "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s160-c/Chess.jpg",
  16. "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s160-c/Chihuly.jpg",
  17. "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s160-c/Closed%252520Door.jpg",
  18. "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s160-c/Colorado%252520River%252520Sunset.jpg",
  19. "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s160-c/Colors%252520of%252520Autumn.jpg",
  20. "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s160-c/Countryside.jpg",
  21. "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s160-c/Death%252520Valley%252520-%252520Dunes.jpg",
  22. "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s160-c/Delicate%252520Arch.jpg",
  23. "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s160-c/Despair.jpg",
  24. "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s160-c/Eagle%252520Fall%252520Sunrise.jpg",
  25. "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s160-c/Electric%252520Storm.jpg",
  26. "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s160-c/False%252520Kiva.jpg",
  27. "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s160-c/Fitzgerald%252520Streaks.jpg",
  28. "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s160-c/Foggy%252520Sunset.jpg",
  29. "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s160-c/Frantic.jpg",
  30. "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s160-c/Golden%252520Gate%252520Afternoon.jpg",
  31. "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s160-c/Golden%252520Gate%252520Fog.jpg",
  32. "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s160-c/Golden%252520Grass.jpg",
  33. "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s160-c/Grand%252520Teton.jpg",
  34. "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s160-c/Grass%252520Closeup.jpg",
  35. "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s160-c/Green%252520Grass.jpg",
  36. "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s160-c/Hanging%252520Leaf.jpg",
  37. "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s160-c/Highway%2525201.jpg",
  38. "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s160-c/Horseshoe%252520Bend%252520Sunset.jpg",
  39. "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s160-c/Horseshoe%252520Bend.jpg",
  40. "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s160-c/Into%252520the%252520Blue.jpg",
  41. "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s160-c/Jelly%252520Fish%2525202.jpg",
  42. "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s160-c/Jelly%252520Fish%2525203.jpg",
  43. "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s160-c/Kauai.jpg",
  44. "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s160-c/Kyoto%252520Sunset.jpg",
  45. "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s160-c/Lake%252520Tahoe%252520Colors.jpg",
  46. "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s160-c/Lava%252520from%252520the%252520Sky.jpg",
  47. "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s160-c/Leica%25252050mm%252520Summilux.jpg",
  48. "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s160-c/Leica%25252050mm%252520Summilux.jpg",
  49. "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s160-c/Leica%252520M8%252520%252528Front%252529.jpg",
  50. "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s160-c/Light%252520to%252520Sand.jpg",
  51. "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s160-c/Little%252520Bit%252520of%252520Paradise.jpg",
  52. "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s160-c/Lone%252520Pine%252520Sunset.jpg",
  53. "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s160-c/Lonely%252520Rock.jpg",
  54. "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s160-c/Longue%252520Vue.jpg",
  55. "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s160-c/Look%252520Me%252520in%252520the%252520Eye.jpg",
  56. "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s160-c/Lost%252520in%252520a%252520Field.jpg",
  57. "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s160-c/Marshall%252520Beach%252520Sunset.jpg",
  58. "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s160-c/Mono%252520Lake%252520Blue.jpg",
  59. "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s160-c/Monument%252520Valley%252520Overlook.jpg",
  60. "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s160-c/Moving%252520Rock.jpg",
  61. "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s160-c/Napali%252520Coast.jpg",
  62. "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s160-c/One%252520Wheel.jpg",
  63. "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s160-c/Open%252520Sky.jpg",
  64. "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s160-c/Orange%252520Sunset.jpg",
  65. "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s160-c/Orchid.jpg",
  66. "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s160-c/Over%252520there.jpg",
  67. "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s160-c/Plumes.jpg",
  68. "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s160-c/Rainbokeh.jpg",
  69. "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s160-c/Rainbow.jpg",
  70. "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s160-c/Rice%252520Fields.jpg",
  71. "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s160-c/Rockaway%252520Fire%252520Sky.jpg",
  72. "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s160-c/Rockaway%252520Flow.jpg",
  73. "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s160-c/Rockaway%252520Sunset%252520Sky.jpg",
  74. "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s160-c/Russian%252520Ridge%252520Sunset.jpg",
  75. "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s160-c/Rust%252520Knot.jpg",
  76. "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s160-c/Sailing%252520Stones.jpg",
  77. "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s160-c/Seahorse.jpg",
  78. "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s160-c/Shinjuku%252520Street.jpg",
  79. "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s160-c/Sierra%252520Heavens.jpg",
  80. "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s160-c/Sierra%252520Sunset.jpg",
  81. "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s160-c/Sin%252520Lights.jpg",
  82. "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s160-c/Starry%252520Lake.jpg",
  83. "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s160-c/Starry%252520Night.jpg",
  84. "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s160-c/Stream.jpg",
  85. "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s160-c/Strip%252520Sunset.jpg",
  86. "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s160-c/Sunset%252520Hills.jpg",
  87. "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s160-c/Tenaya%252520Lake%2525202.jpg",
  88. "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s160-c/Tenaya%252520Lake.jpg",
  89. "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s160-c/The%252520Cave%252520BW.jpg",
  90. "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s160-c/The%252520Fisherman.jpg",
  91. "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s160-c/The%252520Night%252520is%252520Coming.jpg",
  92. "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s160-c/The%252520Road.jpg",
  93. "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s160-c/Tokyo%252520Heights.jpg",
  94. "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s160-c/Tokyo%252520Highway.jpg",
  95. "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s160-c/Tokyo%252520Smog.jpg",
  96. "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s160-c/Tufa%252520at%252520Night.jpg",
  97. "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s160-c/Valley%252520Sunset.jpg",
  98. "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s160-c/Windmill%252520Sunrise.jpg",
  99. "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s160-c/Windmill.jpg",
  100. "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s160-c/Windmills.jpg",
  101. "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s160-c/Yet%252520Another%252520Rockaway%252520Sunset.jpg",
  102. "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s160-c/Yosemite%252520Tree.jpg", };
  103. }<span style="font-family:Times New Roman;font-size:14px;">
  104. </span>
  • ImageDownLoader类,异步下载的核心类,保存图片到手机缓存,将图片加入LruCache中等等
[java] view plaincopy
  1. package com.example.asyncimageloader;
  2. import java.io.IOException;
  3. import java.net.HttpURLConnection;
  4. import java.net.URL;
  5. import java.util.concurrent.ExecutorService;
  6. import java.util.concurrent.Executors;
  7. import android.content.Context;
  8. import android.graphics.Bitmap;
  9. import android.graphics.BitmapFactory;
  10. import android.os.Handler;
  11. import android.os.Message;
  12. import android.support.v4.util.LruCache;
  13. public class ImageDownLoader {
  14. /**
  15. * 缓存Image的类,当存储Image的大小大于LruCache设定的值,系统自动释放内存
  16. */
  17. private LruCache<String, Bitmap> mMemoryCache;
  18. /**
  19. * 操作文件相关类对象的引用
  20. */
  21. private FileUtils fileUtils;
  22. /**
  23. * 下载Image的线程池
  24. */
  25. private ExecutorService mImageThreadPool = null;
  26. public ImageDownLoader(Context context){
  27. //获取系统分配给每个应用程序的最大内存,每个应用系统分配32M
  28. int maxMemory = (int) Runtime.getRuntime().maxMemory();
  29. int mCacheSize = maxMemory / 8;
  30. //给LruCache分配1/8 4M
  31. mMemoryCache = new LruCache<String, Bitmap>(mCacheSize){
  32. //必须重写此方法,来测量Bitmap的大小
  33. @Override
  34. protected int sizeOf(String key, Bitmap value) {
  35. return value.getRowBytes() * value.getHeight();
  36. }
  37. };
  38. fileUtils = new FileUtils(context);
  39. }
  40. /**
  41. * 获取线程池的方法,因为涉及到并发的问题,我们加上同步锁
  42. * @return
  43. */
  44. public ExecutorService getThreadPool(){
  45. if(mImageThreadPool == null){
  46. synchronized(ExecutorService.class){
  47. if(mImageThreadPool == null){
  48. //为了下载图片更加的流畅,我们用了2个线程来下载图片
  49. mImageThreadPool = Executors.newFixedThreadPool(2);
  50. }
  51. }
  52. }
  53. return mImageThreadPool;
  54. }
  55. /**
  56. * 添加Bitmap到内存缓存
  57. * @param key
  58. * @param bitmap
  59. */
  60. public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
  61. if (getBitmapFromMemCache(key) == null && bitmap != null) {
  62. mMemoryCache.put(key, bitmap);
  63. }
  64. }
  65. /**
  66. * 从内存缓存中获取一个Bitmap
  67. * @param key
  68. * @return
  69. */
  70. public Bitmap getBitmapFromMemCache(String key) {
  71. return mMemoryCache.get(key);
  72. }
  73. /**
  74. * 先从内存缓存中获取Bitmap,如果没有就从SD卡或者手机缓存中获取,SD卡或者手机缓存
  75. * 没有就去下载
  76. * @param url
  77. * @param listener
  78. * @return
  79. */
  80. public Bitmap downloadImage(final String url, final onImageLoaderListener listener){
  81. //替换Url中非字母和非数字的字符,这里比较重要,因为我们用Url作为文件名,比如我们的Url
  82. //是Http://xiaanming/abc.jpg;用这个作为图片名称,系统会认为xiaanming为一个目录,
  83. //我们没有创建此目录保存文件就会报错
  84. final String subUrl = url.replaceAll("[^\\w]", "");
  85. Bitmap bitmap = showCacheBitmap(subUrl);
  86. if(bitmap != null){
  87. return bitmap;
  88. }else{
  89. final Handler handler = new Handler(){
  90. @Override
  91. public void handleMessage(Message msg) {
  92. super.handleMessage(msg);
  93. listener.onImageLoader((Bitmap)msg.obj, url);
  94. }
  95. };
  96. getThreadPool().execute(new Runnable() {
  97. @Override
  98. public void run() {
  99. Bitmap bitmap = getBitmapFormUrl(url);
  100. Message msg = handler.obtainMessage();
  101. msg.obj = bitmap;
  102. handler.sendMessage(msg);
  103. try {
  104. //保存在SD卡或者手机目录
  105. fileUtils.savaBitmap(subUrl, bitmap);
  106. } catch (IOException e) {
  107. e.printStackTrace();
  108. }
  109. //将Bitmap 加入内存缓存
  110. addBitmapToMemoryCache(subUrl, bitmap);
  111. }
  112. });
  113. }
  114. return null;
  115. }
  116. /**
  117. * 获取Bitmap, 内存中没有就去手机或者sd卡中获取,这一步在getView中会调用,比较关键的一步
  118. * @param url
  119. * @return
  120. */
  121. public Bitmap showCacheBitmap(String url){
  122. if(getBitmapFromMemCache(url) != null){
  123. return getBitmapFromMemCache(url);
  124. }else if(fileUtils.isFileExists(url) && fileUtils.getFileSize(url) != 0){
  125. //从SD卡获取手机里面获取Bitmap
  126. Bitmap bitmap = fileUtils.getBitmap(url);
  127. //将Bitmap 加入内存缓存
  128. addBitmapToMemoryCache(url, bitmap);
  129. return bitmap;
  130. }
  131. return null;
  132. }
  133. /**
  134. * 从Url中获取Bitmap
  135. * @param url
  136. * @return
  137. */
  138. private Bitmap getBitmapFormUrl(String url) {
  139. Bitmap bitmap = null;
  140. HttpURLConnection con = null;
  141. try {
  142. URL mImageUrl = new URL(url);
  143. con = (HttpURLConnection) mImageUrl.openConnection();
  144. con.setConnectTimeout(10 * 1000);
  145. con.setReadTimeout(10 * 1000);
  146. con.setDoInput(true);
  147. con.setDoOutput(true);
  148. bitmap = BitmapFactory.decodeStream(con.getInputStream());
  149. } catch (Exception e) {
  150. e.printStackTrace();
  151. } finally {
  152. if (con != null) {
  153. con.disconnect();
  154. }
  155. }
  156. return bitmap;
  157. }
  158. /**
  159. * 取消正在下载的任务
  160. */
  161. public synchronized void cancelTask() {
  162. if(mImageThreadPool != null){
  163. mImageThreadPool.shutdownNow();
  164. mImageThreadPool = null;
  165. }
  166. }
  167. /**
  168. * 异步下载图片的回调接口
  169. * @author len
  170. *
  171. */
  172. public interface onImageLoaderListener{
  173. void onImageLoader(Bitmap bitmap, String url);
  174. }
  175. }

ImageDownLoader中有几个方法比较重要

  1. 首先我们需要重写sizeOf(String key, Bitmap value)来计算图片的大小,默认返回图片的数量
  2. downloadImage(final String url, final onImageLoaderListener listener)先去LruCache查看Image,没有再去手机缓存中查看,在没有则开启线程下载,这里我们提供了一个回调接口,回调方法中我们将Bitmap和图片Url作为参数,String subUrl = url.replaceAll("[^\\w]", "") 我在代码中注释写的比较清楚
  3. showCacheBitmap(String url)方法,此方法在Adapter中的getView()当中调用,如果getView()中不调用此方法试试你就知道效果了
  • ImageAdapter  GridView的适配器类,主要是GridView滑动的时候取消下载任务,静止的时候去下载当前显示的item的图片,其他也没什么不同了
[java] view plaincopy
  1. package com.example.asyncimageloader;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.view.View;
  5. import android.view.ViewGroup;
  6. import android.widget.AbsListView;
  7. import android.widget.AbsListView.OnScrollListener;
  8. import android.widget.BaseAdapter;
  9. import android.widget.GridView;
  10. import android.widget.ImageView;
  11. import com.example.asyncimageloader.ImageDownLoader.onImageLoaderListener;
  12. public class ImageAdapter extends BaseAdapter implements OnScrollListener{
  13. /**
  14. * 上下文对象的引用
  15. */
  16. private Context context;
  17. /**
  18. * Image Url的数组
  19. */
  20. private String [] imageThumbUrls;
  21. /**
  22. * GridView对象的应用
  23. */
  24. private GridView mGridView;
  25. /**
  26. * Image 下载器
  27. */
  28. private ImageDownLoader mImageDownLoader;
  29. /**
  30. * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
  31. * 参考http://blog.csdn.net/guolin_blog/article/details/9526203#comments
  32. */
  33. private boolean isFirstEnter = true;
  34. /**
  35. * 一屏中第一个item的位置
  36. */
  37. private int mFirstVisibleItem;
  38. /**
  39. * 一屏中所有item的个数
  40. */
  41. private int mVisibleItemCount;
  42. public ImageAdapter(Context context, GridView mGridView, String [] imageThumbUrls){
  43. this.context = context;
  44. this.mGridView = mGridView;
  45. this.imageThumbUrls = imageThumbUrls;
  46. mImageDownLoader = new ImageDownLoader(context);
  47. mGridView.setOnScrollListener(this);
  48. }
  49. @Override
  50. public void onScrollStateChanged(AbsListView view, int scrollState) {
  51. //仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务
  52. if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
  53. showImage(mFirstVisibleItem, mVisibleItemCount);
  54. }else{
  55. cancelTask();
  56. }
  57. }
  58. /**
  59. * GridView滚动的时候调用的方法,刚开始显示GridView也会调用此方法
  60. */
  61. @Override
  62. public void onScroll(AbsListView view, int firstVisibleItem,
  63. int visibleItemCount, int totalItemCount) {
  64. mFirstVisibleItem = firstVisibleItem;
  65. mVisibleItemCount = visibleItemCount;
  66. // 因此在这里为首次进入程序开启下载任务。
  67. if(isFirstEnter && visibleItemCount > 0){
  68. showImage(mFirstVisibleItem, mVisibleItemCount);
  69. isFirstEnter = false;
  70. }
  71. }
  72. @Override
  73. public int getCount() {
  74. return imageThumbUrls.length;
  75. }
  76. @Override
  77. public Object getItem(int position) {
  78. return imageThumbUrls[position];
  79. }
  80. @Override
  81. public long getItemId(int position) {
  82. return position;
  83. }
  84. @Override
  85. public View getView(int position, View convertView, ViewGroup parent) {
  86. ImageView mImageView;
  87. final String mImageUrl = imageThumbUrls[position];
  88. if(convertView == null){
  89. mImageView = new ImageView(context);
  90. }else{
  91. mImageView = (ImageView) convertView;
  92. }
  93. mImageView.setLayoutParams(new GridView.LayoutParams(150, 150));
  94. mImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
  95. //给ImageView设置Tag,这里已经是司空见惯了
  96. mImageView.setTag(mImageUrl);
  97. /*******************************去掉下面这几行试试是什么效果****************************/
  98. Bitmap bitmap = mImageDownLoader.showCacheBitmap(mImageUrl.replaceAll("[^\\w]", ""));
  99. if(bitmap != null){
  100. mImageView.setImageBitmap(bitmap);
  101. }else{
  102. mImageView.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_empty));
  103. }
  104. /**********************************************************************************/
  105. return mImageView;
  106. }
  107. /**
  108. * 显示当前屏幕的图片,先会去查找LruCache,LruCache没有就去sd卡或者手机目录查找,在没有就开启线程去下载
  109. * @param firstVisibleItem
  110. * @param visibleItemCount
  111. */
  112. private void showImage(int firstVisibleItem, int visibleItemCount){
  113. Bitmap bitmap = null;
  114. for(int i=firstVisibleItem; i<firstVisibleItem + visibleItemCount; i++){
  115. String mImageUrl = imageThumbUrls[i];
  116. final ImageView mImageView = (ImageView) mGridView.findViewWithTag(mImageUrl);
  117. bitmap = mImageDownLoader.downloadImage(mImageUrl, new onImageLoaderListener() {
  118. @Override
  119. public void onImageLoader(Bitmap bitmap, String url) {
  120. if(mImageView != null && bitmap != null){
  121. mImageView.setImageBitmap(bitmap);
  122. }
  123. }
  124. });
  125. //if(bitmap != null){
  126. //  mImageView.setImageBitmap(bitmap);
  127. //}else{
  128. //  mImageView.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_empty));
  129. //}
  130. }
  131. }
  132. /**
  133. * 取消下载任务
  134. */
  135. public void cancelTask(){
  136. mImageDownLoader.cancelTask();
  137. }
  138. }
  • MainActivity  里面一个GridView,然后提供一个系统菜单来删除手机上的缓存图片,直接上代码,比较简单所以里面也没有注释
[java] view plaincopy
  1. package com.example.asyncimageloader;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.Menu;
  5. import android.view.MenuItem;
  6. import android.widget.GridView;
  7. import android.widget.Toast;
  8. public class MainActivity extends Activity {
  9. private GridView mGridView;
  10. private String [] imageThumbUrls = Images.imageThumbUrls;
  11. private ImageAdapter mImageAdapter;
  12. private FileUtils fileUtils;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_main);
  17. fileUtils = new FileUtils(this);
  18. mGridView = (GridView) findViewById(R.id.gridView);
  19. mImageAdapter = new ImageAdapter(this, mGridView, imageThumbUrls);
  20. mGridView.setAdapter(mImageAdapter);
  21. }
  22. @Override
  23. protected void onDestroy() {
  24. mImageAdapter.cancelTask();
  25. super.onDestroy();
  26. }
  27. @Override
  28. public boolean onCreateOptionsMenu(Menu menu) {
  29. super.onCreateOptionsMenu(menu);
  30. menu.add("删除手机中图片缓存");
  31. return super.onCreateOptionsMenu(menu);
  32. }
  33. @Override
  34. public boolean onOptionsItemSelected(MenuItem item) {
  35. switch (item.getItemId()) {
  36. case 0:
  37. fileUtils.deleteFile();
  38. Toast.makeText(getApplication(), "清空缓存成功", Toast.LENGTH_SHORT).show();
  39. break;
  40. }
  41. return super.onOptionsItemSelected(item);
  42. }
  43. }<span style="font-family:Times New Roman;font-size:14px;">
  44. </span>
  • MainActivity的布局文件
[html] view plaincopy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. tools:context=".MainActivity" >
  6. <GridView
  7. android:id="@+id/gridView"
  8. android:layout_width="match_parent"
  9. android:layout_height="match_parent"
  10. android:stretchMode="columnWidth"
  11. android:columnWidth="90dip"
  12. android:verticalSpacing="10dip"
  13. android:horizontalSpacing="10dip"
  14. android:cacheColorHint="@android:color/transparent"
  15. android:numColumns="auto_fit" >
  16. </GridView>
  17. </RelativeLayout>
  • 因为有网络操作和对sd卡的操作,所以我们必须加上相对应的权限
[html] view plaincopy
  1. <uses-permission android:name="android.permission.INTERNET" />
  2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

好了,代码就全部介绍完了,我们运行下程序看看效果,这里的效果是用一个线程下载图片的效果,可能有一些不完整,想看看效果自己动手试试,郁闷,家里的网络卡,gif的图片出不来效果,只能明天上传图片啦,公司也上传不了动画效果,我去,那我就贴一张普通图片,哈哈

好了,今天的讲解到此结束,有疑问的朋友请在下面留言,源码里面GridView的布局文件 columnWedth设置了120dip,有点丑,大家可以改成90dip

源码下载,点击这里

Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅相关推荐

  1. [置顶] 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅

    转载请注明出处http://blog.csdn.net/xiaanming/article/details/9825113 异步加载图片的例子,网上也比较多,大部分用了HashMap<Strin ...

  2. Android:异步加载图片

    我们知道Android为了不阻塞UI线程(main线程),不允许在非UI线程中进行UI操作以及网络请求等操作,为了不阻塞UI,我们往往就要进行异步加载. 我们以异步加载图片为例子,来学习一下异步加载 ...

  3. wemall app商城源码中基于JAVA的Android异步加载图片管理器代码

    wemall doraemon是Android客户端程序,服务端采用wemall微信商城,不对原商城做任何修改,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可随意定制修改.本文分享其中 ...

  4. android异步加载图片并缓存到内存和sd卡上,Android批量图片加载经典系列——采用二级缓存、异步加载网络图片...

    http://www.cnblogs.com/jerehedu/p/4560119.html 2015-06-08 09:20 by 杰瑞教育, 232 阅读, 1 评论, 收藏, 编辑一.问题描述 ...

  5. 演化理解 Android 异步加载图片

    引用:http://www.cnblogs.com/ghj1976/archive/2011/05/06/2038738.html 下面测试使用的layout文件: 简单来说就是 LinearLayo ...

  6. Android 异步加载图片分析

    研究了android从网络上异步加载图像,现总结如下: (1)由于android UI更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法. 在主线程中new ...

  7. android图片异步加载图片,Android 异步加载图片分析总结

    研究了android从网络上异步加载图像,现总结如下: (1)由于android UI更新支持单一线程原则,所以从网络上取数据并更新到界面上,为了不阻塞主线程首先可能会想到以下方法. 在主线程中new ...

  8. android 异步加载图片总结

    一Handler+Runnable模式 我们先看一个并不是异步线程加载的例子,使用 Handler+Runnable模式. 这里为何不是新开线程的原因请参看这篇文章:Android Runnable ...

  9. android 异步加载图片缩略图

    建一个AsyncLoadedImage类继承AsyncTask异步加载类,调用publishProgress方法更新onProgressUpdate贮存缩略图信息到Adapter.监听Adapter ...

  10. Android之ListView异步加载图片且仅显示可见子项中的图片

    折腾了好多天,遇到 N 多让人崩溃无语的问题,不过今天终于有些收获了,这是实验的第一版,有些混乱,下一步进行改造细分,先把代码记录在这儿吧. 网上查了很多资料,发现都千篇一律,抄来抄去,很多细节和完整 ...

最新文章

  1. R语言可视化散点图(scatter plot)、并在散点图中叠加回归曲线、叠加lowess拟合曲线(linear and lowess fit lines)、使用plot、line、abline函数
  2. XML转JSON的javascript代码
  3. 主机电源全是黑线怎么短接_汽车胎压监测即将成为强制标准,听听老司机怎么说...
  4. 华为手机销量超过苹果,华为能算是全球第二大手机厂家吗?
  5. try catch finally
  6. 年轻人有梦想,老人有回忆
  7. mysql的事务与锁机制
  8. MySQL性能半同步复制VS异步复制
  9. 服装商城电商-前端网页技术精美完整源码HTML+CSS+JS
  10. intel 82599网卡系统下丢失一路万兆端口
  11. IDEA打包失败解决方案
  12. 计算机多道程序设计是指什么,多道程序设计是什么意思?
  13. 添加打印机,错误为0x0000011b
  14. python3排序,sorted字典排序
  15. 51单片机C语言us级延时函数
  16. 笔记本计算机摄像头怎么打开,笔记本电脑的摄像头在哪里打开(图解摄像头开启步骤)...
  17. Astalavista被蹂躏过程 转载自baoz net
  18. 软件工程专业期末项目开发全流程模拟日志(第一天)
  19. 【Android】安卓webview播放视频白屏解决方法
  20. 记录一下自己2018年秋招的情况

热门文章

  1. VGA高速PCB布局布线设计指南
  2. matlab 开普勒方程,第二章 开普勒方程.ppt
  3. 周杰伦新专辑预售热点传播分析报告概览
  4. 一个整数“犯二的程度”
  5. sqlserver 自定义日期函数(MMdd)
  6. Java 总结4 数据流 文件处理
  7. iOS开发常用网址(最全)
  8. Win10更新失败的解决办法
  9. 三天撸完了MyBatis,各位随便问!!(冰河吐血整理,建议收藏)
  10. 如何对计算机进行远程连接,如何实现远程连接 远程连接命令介绍