异步加载图片的例子,网上也比较多,大部分用了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. 降低了代码的耦合性,结构更加的清晰,便于以后重用

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

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

  • FileUtils 文件操作的工具类,提供保存图片,获取图片,判断图片是否存在,删除图片的一些方法,这个类比较简单

1. package
2.
3. import
4. import
5. import
6.
7. import
8. import
9. import
10. import
11. import
12.
13. public class
14. /**
15. * sd卡的根目录
16. */
17. private static
18. /**
19. * 手机的缓存根目录
20. */
21. private static String mDataRootPath = null;
22. /**
23. * 保存Image的目录名
24. */
25. private final static String FOLDER_NAME = "/AndroidImage";
26.
27.
28. public
29. mDataRootPath = context.getCacheDir().getPath();
30. }
31.
32.
33. /**
34. * 获取储存Image的目录
35. * @return
36. */
37. private
38. return
39. mSdRootPath + FOLDER_NAME : mDataRootPath + FOLDER_NAME;
40. }
41.
42. /**
43. * 保存Image的方法,有sd卡存储到sd卡,没有就存储到手机目录
44. * @param fileName
45. * @param bitmap
46. * @throws IOException
47. */
48. public void savaBitmap(String fileName, Bitmap bitmap) throws
49. if(bitmap == null){
50. return;
51. }
52. String path = getStorageDirectory();
53. new
54. if(!folderFile.exists()){
55. folderFile.mkdir();
56. }
57. new
58. file.createNewFile();
59. new
60. 100, fos);
61. fos.flush();
62. fos.close();
63. }
64.
65. /**
66. * 从手机或者sd卡获取Bitmap
67. * @param fileName
68. * @return
69. */
70. public
71. return
72. }
73.
74. /**
75. * 判断文件是否存在
76. * @param fileName
77. * @return
78. */
79. public boolean
80. return new
81. }
82.
83. /**
84. * 获取文件的大小
85. * @param fileName
86. * @return
87. */
88. public long
89. return new
90. }
91.
92.
93. /**
94. * 删除SD卡或者手机的缓存图片和目录
95. */
96. public void
97. new
98. if(! dirFile.exists()){
99. return;
100. }
101. if
102. String[] children = dirFile.list();
103. for (int i = 0; i < children.length; i++) {
104. new
105. }
106. }
107.
108. dirFile.delete();
109. }
110. }<span style="font-family:Times New Roman;font-size:14px;">
111. </span>

1. package
2.
3. public class
4.
5. public final static String[] imageThumbUrls = new
6. "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s160-c/A%252520Photographer.jpg",
7. "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s160-c/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
8. "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s160-c/Another%252520Rockaway%252520Sunset.jpg",
9. "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s160-c/Antelope%252520Butte.jpg",
10. "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s160-c/Antelope%252520Hallway.jpg",
11. "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s160-c/Antelope%252520Walls.jpg",
12. "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s160-c/Apre%2525CC%252580s%252520la%252520Pluie.jpg",
13. "https://lh3.googleusercontent.com/-s-AFpvgSeew/URquc6dF-JI/AAAAAAAAAbs/Mt3xNGRUd68/s160-c/Backlit%252520Cloud.jpg",
14. "https://lh5.googleusercontent.com/-bvmif9a9YOQ/URquea3heHI/AAAAAAAAAbs/rcr6wyeQtAo/s160-c/Bee%252520and%252520Flower.jpg",
15. "https://lh5.googleusercontent.com/-n7mdm7I7FGs/URqueT_BT-I/AAAAAAAAAbs/9MYmXlmpSAo/s160-c/Bonzai%252520Rock%252520Sunset.jpg",
16. "https://lh6.googleusercontent.com/-4CN4X4t0M1k/URqufPozWzI/AAAAAAAAAbs/8wK41lg1KPs/s160-c/Caterpillar.jpg",
17. "https://lh3.googleusercontent.com/-rrFnVC8xQEg/URqufdrLBaI/AAAAAAAAAbs/s69WYy_fl1E/s160-c/Chess.jpg",
18. "https://lh5.googleusercontent.com/-WVpRptWH8Yw/URqugh-QmDI/AAAAAAAAAbs/E-MgBgtlUWU/s160-c/Chihuly.jpg",
19. "https://lh5.googleusercontent.com/-0BDXkYmckbo/URquhKFW84I/AAAAAAAAAbs/ogQtHCTk2JQ/s160-c/Closed%252520Door.jpg",
20. "https://lh3.googleusercontent.com/-PyggXXZRykM/URquh-kVvoI/AAAAAAAAAbs/hFtDwhtrHHQ/s160-c/Colorado%252520River%252520Sunset.jpg",
21. "https://lh3.googleusercontent.com/-ZAs4dNZtALc/URquikvOCWI/AAAAAAAAAbs/DXz4h3dll1Y/s160-c/Colors%252520of%252520Autumn.jpg",
22. "https://lh4.googleusercontent.com/-GztnWEIiMz8/URqukVCU7bI/AAAAAAAAAbs/jo2Hjv6MZ6M/s160-c/Countryside.jpg",
23. "https://lh4.googleusercontent.com/-bEg9EZ9QoiM/URquklz3FGI/AAAAAAAAAbs/UUuv8Ac2BaE/s160-c/Death%252520Valley%252520-%252520Dunes.jpg",
24. "https://lh6.googleusercontent.com/-ijQJ8W68tEE/URqulGkvFEI/AAAAAAAAAbs/zPXvIwi_rFw/s160-c/Delicate%252520Arch.jpg",
25. "https://lh5.googleusercontent.com/-Oh8mMy2ieng/URqullDwehI/AAAAAAAAAbs/TbdeEfsaIZY/s160-c/Despair.jpg",
26. "https://lh5.googleusercontent.com/-gl0y4UiAOlk/URqumC_KjBI/AAAAAAAAAbs/PM1eT7dn4oo/s160-c/Eagle%252520Fall%252520Sunrise.jpg",
27. "https://lh3.googleusercontent.com/-hYYHd2_vXPQ/URqumtJa9eI/AAAAAAAAAbs/wAalXVkbSh0/s160-c/Electric%252520Storm.jpg",
28. "https://lh5.googleusercontent.com/-PyY_yiyjPTo/URqunUOhHFI/AAAAAAAAAbs/azZoULNuJXc/s160-c/False%252520Kiva.jpg",
29. "https://lh6.googleusercontent.com/-PYvLVdvXywk/URqunwd8hfI/AAAAAAAAAbs/qiMwgkFvf6I/s160-c/Fitzgerald%252520Streaks.jpg",
30. "https://lh4.googleusercontent.com/-KIR_UobIIqY/URquoCZ9SlI/AAAAAAAAAbs/Y4d4q8sXu4c/s160-c/Foggy%252520Sunset.jpg",
31. "https://lh6.googleusercontent.com/-9lzOk_OWZH0/URquoo4xYoI/AAAAAAAAAbs/AwgzHtNVCwU/s160-c/Frantic.jpg",
32. "https://lh3.googleusercontent.com/-0X3JNaKaz48/URqupH78wpI/AAAAAAAAAbs/lHXxu_zbH8s/s160-c/Golden%252520Gate%252520Afternoon.jpg",
33. "https://lh6.googleusercontent.com/-95sb5ag7ABc/URqupl95RDI/AAAAAAAAAbs/g73R20iVTRA/s160-c/Golden%252520Gate%252520Fog.jpg",
34. "https://lh3.googleusercontent.com/-JB9v6rtgHhk/URqup21F-zI/AAAAAAAAAbs/64Fb8qMZWXk/s160-c/Golden%252520Grass.jpg",
35. "https://lh4.googleusercontent.com/-EIBGfnuLtII/URquqVHwaRI/AAAAAAAAAbs/FA4McV2u8VE/s160-c/Grand%252520Teton.jpg",
36. "https://lh4.googleusercontent.com/-WoMxZvmN9nY/URquq1v2AoI/AAAAAAAAAbs/grj5uMhL6NA/s160-c/Grass%252520Closeup.jpg",
37. "https://lh3.googleusercontent.com/-6hZiEHXx64Q/URqurxvNdqI/AAAAAAAAAbs/kWMXM3o5OVI/s160-c/Green%252520Grass.jpg",
38. "https://lh5.googleusercontent.com/-6LVb9OXtQ60/URquteBFuKI/AAAAAAAAAbs/4F4kRgecwFs/s160-c/Hanging%252520Leaf.jpg",
39. "https://lh4.googleusercontent.com/-zAvf__52ONk/URqutT_IuxI/AAAAAAAAAbs/D_bcuc0thoU/s160-c/Highway%2525201.jpg",
40. "https://lh6.googleusercontent.com/-H4SrUg615rA/URquuL27fXI/AAAAAAAAAbs/4aEqJfiMsOU/s160-c/Horseshoe%252520Bend%252520Sunset.jpg",
41. "https://lh4.googleusercontent.com/-JhFi4fb_Pqw/URquuX-QXbI/AAAAAAAAAbs/IXpYUxuweYM/s160-c/Horseshoe%252520Bend.jpg",
42. "https://lh5.googleusercontent.com/-UGgssvFRJ7g/URquueyJzGI/AAAAAAAAAbs/yYIBlLT0toM/s160-c/Into%252520the%252520Blue.jpg",
43. "https://lh3.googleusercontent.com/-CH7KoupI7uI/URquu0FF__I/AAAAAAAAAbs/R7GDmI7v_G0/s160-c/Jelly%252520Fish%2525202.jpg",
44. "https://lh4.googleusercontent.com/-pwuuw6yhg8U/URquvPxR3FI/AAAAAAAAAbs/VNGk6f-tsGE/s160-c/Jelly%252520Fish%2525203.jpg",
45. "https://lh5.googleusercontent.com/-GoUQVw1fnFw/URquv6xbC0I/AAAAAAAAAbs/zEUVTQQ43Zc/s160-c/Kauai.jpg",
46. "https://lh6.googleusercontent.com/-8QdYYQEpYjw/URquwvdh88I/AAAAAAAAAbs/cktDy-ysfHo/s160-c/Kyoto%252520Sunset.jpg",
47. "https://lh4.googleusercontent.com/-vPeekyDjOE0/URquwzJ28qI/AAAAAAAAAbs/qxcyXULsZrg/s160-c/Lake%252520Tahoe%252520Colors.jpg",
48. "https://lh4.googleusercontent.com/-xBPxWpD4yxU/URquxWHk8AI/AAAAAAAAAbs/ARDPeDYPiMY/s160-c/Lava%252520from%252520the%252520Sky.jpg",
49. "https://lh3.googleusercontent.com/-897VXrJB6RE/URquxxxd-5I/AAAAAAAAAbs/j-Cz4T4YvIw/s160-c/Leica%25252050mm%252520Summilux.jpg",
50. "https://lh5.googleusercontent.com/-qSJ4D4iXzGo/URquyDWiJ1I/AAAAAAAAAbs/k2pBXeWehOA/s160-c/Leica%25252050mm%252520Summilux.jpg",
51. "https://lh6.googleusercontent.com/-dwlPg83vzLg/URquylTVuFI/AAAAAAAAAbs/G6SyQ8b4YsI/s160-c/Leica%252520M8%252520%252528Front%252529.jpg",
52. "https://lh3.googleusercontent.com/-R3_EYAyJvfk/URquzQBv8eI/AAAAAAAAAbs/b9xhpUM3pEI/s160-c/Light%252520to%252520Sand.jpg",
53. "https://lh3.googleusercontent.com/-fHY5h67QPi0/URqu0Cp4J1I/AAAAAAAAAbs/0lG6m94Z6vM/s160-c/Little%252520Bit%252520of%252520Paradise.jpg",
54. "https://lh5.googleusercontent.com/-TzF_LwrCnRM/URqu0RddPOI/AAAAAAAAAbs/gaj2dLiuX0s/s160-c/Lone%252520Pine%252520Sunset.jpg",
55. "https://lh3.googleusercontent.com/-4HdpJ4_DXU4/URqu046dJ9I/AAAAAAAAAbs/eBOodtk2_uk/s160-c/Lonely%252520Rock.jpg",
56. "https://lh6.googleusercontent.com/-erbF--z-W4s/URqu1ajSLkI/AAAAAAAAAbs/xjDCDO1INzM/s160-c/Longue%252520Vue.jpg",
57. "https://lh6.googleusercontent.com/-0CXJRdJaqvc/URqu1opNZNI/AAAAAAAAAbs/PFB2oPUU7Lk/s160-c/Look%252520Me%252520in%252520the%252520Eye.jpg",
58. "https://lh3.googleusercontent.com/-D_5lNxnDN6g/URqu2Tk7HVI/AAAAAAAAAbs/p0ddca9W__Y/s160-c/Lost%252520in%252520a%252520Field.jpg",
59. "https://lh6.googleusercontent.com/-flsqwMrIk2Q/URqu24PcmjI/AAAAAAAAAbs/5ocIH85XofM/s160-c/Marshall%252520Beach%252520Sunset.jpg",
60. "https://lh4.googleusercontent.com/-Y4lgryEVTmU/URqu28kG3gI/AAAAAAAAAbs/OjXpekqtbJ4/s160-c/Mono%252520Lake%252520Blue.jpg",
61. "https://lh4.googleusercontent.com/-AaHAJPmcGYA/URqu3PIldHI/AAAAAAAAAbs/lcTqk1SIcRs/s160-c/Monument%252520Valley%252520Overlook.jpg",
62. "https://lh4.googleusercontent.com/-vKxfdQ83dQA/URqu31Yq_BI/AAAAAAAAAbs/OUoGk_2AyfM/s160-c/Moving%252520Rock.jpg",
63. "https://lh5.googleusercontent.com/-CG62QiPpWXg/URqu4ia4vRI/AAAAAAAAAbs/0YOdqLAlcAc/s160-c/Napali%252520Coast.jpg",
64. "https://lh6.googleusercontent.com/-wdGrP5PMmJQ/URqu5PZvn7I/AAAAAAAAAbs/m0abEcdPXe4/s160-c/One%252520Wheel.jpg",
65. "https://lh6.googleusercontent.com/-6WS5DoCGuOA/URqu5qx1UgI/AAAAAAAAAbs/giMw2ixPvrY/s160-c/Open%252520Sky.jpg",
66. "https://lh6.googleusercontent.com/-u8EHKj8G8GQ/URqu55sM6yI/AAAAAAAAAbs/lIXX_GlTdmI/s160-c/Orange%252520Sunset.jpg",
67. "https://lh6.googleusercontent.com/-74Z5qj4bTDE/URqu6LSrJrI/AAAAAAAAAbs/XzmVkw90szQ/s160-c/Orchid.jpg",
68. "https://lh6.googleusercontent.com/-lEQE4h6TePE/URqu6t_lSkI/AAAAAAAAAbs/zvGYKOea_qY/s160-c/Over%252520there.jpg",
69. "https://lh5.googleusercontent.com/-cauH-53JH2M/URqu66v_USI/AAAAAAAAAbs/EucwwqclfKQ/s160-c/Plumes.jpg",
70. "https://lh3.googleusercontent.com/-eDLT2jHDoy4/URqu7axzkAI/AAAAAAAAAbs/iVZE-xJ7lZs/s160-c/Rainbokeh.jpg",
71. "https://lh5.googleusercontent.com/-j1NLqEFIyco/URqu8L1CGcI/AAAAAAAAAbs/aqZkgX66zlI/s160-c/Rainbow.jpg",
72. "https://lh5.googleusercontent.com/-DRnqmK0t4VU/URqu8XYN9yI/AAAAAAAAAbs/LgvF_592WLU/s160-c/Rice%252520Fields.jpg",
73. "https://lh3.googleusercontent.com/-hwh1v3EOGcQ/URqu8qOaKwI/AAAAAAAAAbs/IljRJRnbJGw/s160-c/Rockaway%252520Fire%252520Sky.jpg",
74. "https://lh5.googleusercontent.com/-wjV6FQk7tlk/URqu9jCQ8sI/AAAAAAAAAbs/RyYUpdo-c9o/s160-c/Rockaway%252520Flow.jpg",
75. "https://lh6.googleusercontent.com/-6cAXNfo7D20/URqu-BdzgPI/AAAAAAAAAbs/OmsYllzJqwo/s160-c/Rockaway%252520Sunset%252520Sky.jpg",
76. "https://lh3.googleusercontent.com/-sl8fpGPS-RE/URqu_BOkfgI/AAAAAAAAAbs/Dg2Fv-JxOeg/s160-c/Russian%252520Ridge%252520Sunset.jpg",
77. "https://lh6.googleusercontent.com/-gVtY36mMBIg/URqu_q91lkI/AAAAAAAAAbs/3CiFMBcy5MA/s160-c/Rust%252520Knot.jpg",
78. "https://lh6.googleusercontent.com/-GHeImuHqJBE/URqu_FKfVLI/AAAAAAAAAbs/axuEJeqam7Q/s160-c/Sailing%252520Stones.jpg",
79. "https://lh3.googleusercontent.com/-hBbYZjTOwGc/URqu_ycpIrI/AAAAAAAAAbs/nAdJUXnGJYE/s160-c/Seahorse.jpg",
80. "https://lh3.googleusercontent.com/-Iwi6-i6IexY/URqvAYZHsVI/AAAAAAAAAbs/5ETWl4qXsFE/s160-c/Shinjuku%252520Street.jpg",
81. "https://lh6.googleusercontent.com/-amhnySTM_MY/URqvAlb5KoI/AAAAAAAAAbs/pFCFgzlKsn0/s160-c/Sierra%252520Heavens.jpg",
82. "https://lh5.googleusercontent.com/-dJgjepFrYSo/URqvBVJZrAI/AAAAAAAAAbs/v-F5QWpYO6s/s160-c/Sierra%252520Sunset.jpg",
83. "https://lh4.googleusercontent.com/-Z4zGiC5nWdc/URqvBdEwivI/AAAAAAAAAbs/ZRZR1VJ84QA/s160-c/Sin%252520Lights.jpg",
84. "https://lh4.googleusercontent.com/-_0cYiWW8ccY/URqvBz3iM4I/AAAAAAAAAbs/9N_Wq8MhLTY/s160-c/Starry%252520Lake.jpg",
85. "https://lh3.googleusercontent.com/-A9LMoRyuQUA/URqvCYx_JoI/AAAAAAAAAbs/s7sde1Bz9cI/s160-c/Starry%252520Night.jpg",
86. "https://lh3.googleusercontent.com/-KtLJ3k858eY/URqvC_2h_bI/AAAAAAAAAbs/zzEBImwDA_g/s160-c/Stream.jpg",
87. "https://lh5.googleusercontent.com/-dFB7Lad6RcA/URqvDUftwWI/AAAAAAAAAbs/BrhoUtXTN7o/s160-c/Strip%252520Sunset.jpg",
88. "https://lh5.googleusercontent.com/-at6apgFiN20/URqvDyffUZI/AAAAAAAAAbs/clABCx171bE/s160-c/Sunset%252520Hills.jpg",
89. "https://lh4.googleusercontent.com/-7-EHhtQthII/URqvEYTk4vI/AAAAAAAAAbs/QSJZoB3YjVg/s160-c/Tenaya%252520Lake%2525202.jpg",
90. "https://lh6.googleusercontent.com/-8MrjV_a-Pok/URqvFC5repI/AAAAAAAAAbs/9inKTg9fbCE/s160-c/Tenaya%252520Lake.jpg",
91. "https://lh5.googleusercontent.com/-B1HW-z4zwao/URqvFWYRwUI/AAAAAAAAAbs/8Peli53Bs8I/s160-c/The%252520Cave%252520BW.jpg",
92. "https://lh3.googleusercontent.com/-PO4E-xZKAnQ/URqvGRqjYkI/AAAAAAAAAbs/42nyADFsXag/s160-c/The%252520Fisherman.jpg",
93. "https://lh4.googleusercontent.com/-iLyZlzfdy7s/URqvG0YScdI/AAAAAAAAAbs/1J9eDKmkXtk/s160-c/The%252520Night%252520is%252520Coming.jpg",
94. "https://lh6.googleusercontent.com/-G-k7YkkUco0/URqvHhah6fI/AAAAAAAAAbs/_taQQG7t0vo/s160-c/The%252520Road.jpg",
95. "https://lh6.googleusercontent.com/-h-ALJt7kSus/URqvIThqYfI/AAAAAAAAAbs/ejiv35olWS8/s160-c/Tokyo%252520Heights.jpg",
96. "https://lh5.googleusercontent.com/-Hy9k-TbS7xg/URqvIjQMOxI/AAAAAAAAAbs/RSpmmOATSkg/s160-c/Tokyo%252520Highway.jpg",
97. "https://lh6.googleusercontent.com/-83oOvMb4OZs/URqvJL0T7lI/AAAAAAAAAbs/c5TECZ6RONM/s160-c/Tokyo%252520Smog.jpg",
98. "https://lh3.googleusercontent.com/-FB-jfgREEfI/URqvJI3EXAI/AAAAAAAAAbs/XfyweiRF4v8/s160-c/Tufa%252520at%252520Night.jpg",
99. "https://lh4.googleusercontent.com/-vngKD5Z1U8w/URqvJUCEgPI/AAAAAAAAAbs/ulxCMVcU6EU/s160-c/Valley%252520Sunset.jpg",
100. "https://lh6.googleusercontent.com/-DOz5I2E2oMQ/URqvKMND1kI/AAAAAAAAAbs/Iqf0IsInleo/s160-c/Windmill%252520Sunrise.jpg",
101. "https://lh5.googleusercontent.com/-biyiyWcJ9MU/URqvKculiAI/AAAAAAAAAbs/jyPsCplJOpE/s160-c/Windmill.jpg",
102. "https://lh4.googleusercontent.com/-PDT167_xRdA/URqvK36mLcI/AAAAAAAAAbs/oi2ik9QseMI/s160-c/Windmills.jpg",
103. "https://lh5.googleusercontent.com/-kI_QdYx7VlU/URqvLXCB6gI/AAAAAAAAAbs/N31vlZ6u89o/s160-c/Yet%252520Another%252520Rockaway%252520Sunset.jpg",
104. "https://lh4.googleusercontent.com/-e9NHZ5k5MSs/URqvMIBZjtI/AAAAAAAAAbs/1fV810rDNfQ/s160-c/Yosemite%252520Tree.jpg", };
105.
106. }<span style="font-family:Times New Roman;font-size:14px;">
107. </span>
  • ImageDownLoader类,异步下载的核心类,保存图片到手机缓存,将图片加入LruCache中等等

1. package
2.
3. import
4. import
5. import
6. import
7. import
8.
9. import
10. import
11. import
12. import
13. import
14. import
15.
16. public class
17. /**
18. * 缓存Image的类,当存储Image的大小大于LruCache设定的值,系统自动释放内存
19. */
20. private
21. /**
22. * 操作文件相关类对象的引用
23. */
24. private
25. /**
26. * 下载Image的线程池
27. */
28. private ExecutorService mImageThreadPool = null;
29.
30.
31. public
32. //获取系统分配给每个应用程序的最大内存,每个应用系统分配32M
33. int maxMemory = (int) Runtime.getRuntime().maxMemory();
34. int mCacheSize = maxMemory / 8;
35. //给LruCache分配1/8 4M
36. new
37.
38. //必须重写此方法,来测量Bitmap的大小
39. @Override
40. protected int
41. return
42. }
43.
44. };
45.
46. new
47. }
48.
49.
50. /**
51. * 获取线程池的方法,因为涉及到并发的问题,我们加上同步锁
52. * @return
53. */
54. public
55. if(mImageThreadPool == null){
56. synchronized(ExecutorService.class){
57. if(mImageThreadPool == null){
58. //为了下载图片更加的流畅,我们用了2个线程来下载图片
59. 2);
60. }
61. }
62. }
63.
64. return
65.
66. }
67.
68. /**
69. * 添加Bitmap到内存缓存
70. * @param key
71. * @param bitmap
72. */
73. public void
74. if (getBitmapFromMemCache(key) == null && bitmap != null) {
75. mMemoryCache.put(key, bitmap);
76. }
77. }
78.
79. /**
80. * 从内存缓存中获取一个Bitmap
81. * @param key
82. * @return
83. */
84. public
85. return
86. }
87.
88. /**
89. * 先从内存缓存中获取Bitmap,如果没有就从SD卡或者手机缓存中获取,SD卡或者手机缓存
90. * 没有就去下载
91. * @param url
92. * @param listener
93. * @return
94. */
95. public Bitmap downloadImage(final String url, final
96. //替换Url中非字母和非数字的字符,这里比较重要,因为我们用Url作为文件名,比如我们的Url
97. //是Http://xiaanming/abc.jpg;用这个作为图片名称,系统会认为xiaanming为一个目录,
98. //我们没有创建此目录保存文件就会报错
99. final String subUrl = url.replaceAll("[^\\w]", "");
100. Bitmap bitmap = showCacheBitmap(subUrl);
101. if(bitmap != null){
102. return
103. else{
104.
105. final Handler handler = new
106. @Override
107. public void
108. super.handleMessage(msg);
109. listener.onImageLoader((Bitmap)msg.obj, url);
110. }
111. };
112.
113. new
114.
115. @Override
116. public void
117. Bitmap bitmap = getBitmapFormUrl(url);
118. Message msg = handler.obtainMessage();
119. msg.obj = bitmap;
120. handler.sendMessage(msg);
121.
122. try
123. //保存在SD卡或者手机目录
124. fileUtils.savaBitmap(subUrl, bitmap);
125. catch
126. e.printStackTrace();
127. }
128.
129. //将Bitmap 加入内存缓存
130. addBitmapToMemoryCache(subUrl, bitmap);
131. }
132. });
133. }
134.
135. return null;
136. }
137.
138. /**
139. * 获取Bitmap, 内存中没有就去手机或者sd卡中获取,这一步在getView中会调用,比较关键的一步
140. * @param url
141. * @return
142. */
143. public
144. if(getBitmapFromMemCache(url) != null){
145. return
146. else if(fileUtils.isFileExists(url) && fileUtils.getFileSize(url) != 0){
147. //从SD卡获取手机里面获取Bitmap
148. Bitmap bitmap = fileUtils.getBitmap(url);
149.
150. //将Bitmap 加入内存缓存
151. addBitmapToMemoryCache(url, bitmap);
152. return
153. }
154.
155. return null;
156. }
157.
158.
159. /**
160. * 从Url中获取Bitmap
161. * @param url
162. * @return
163. */
164. private
165. null;
166. null;
167. try
168. new
169. con = (HttpURLConnection) mImageUrl.openConnection();
170. 10 * 1000);
171. 10 * 1000);
172. true);
173. true);
174. bitmap = BitmapFactory.decodeStream(con.getInputStream());
175. catch
176. e.printStackTrace();
177. finally
178. if (con != null) {
179. con.disconnect();
180. }
181. }
182. return
183. }
184.
185. /**
186. * 取消正在下载的任务
187. */
188. public synchronized void
189. if(mImageThreadPool != null){
190. mImageThreadPool.shutdownNow();
191. null;
192. }
193. }
194.
195.
196. /**
197. * 异步下载图片的回调接口
198. * @author len
199. *
200. */
201. public interface
202. void
203. }
204.
205. }

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的图片,其他也没什么不同了

2.   
3. import
4. import
5. import
6. import
7. import
8. import
9. import
10. import
11. import
12.
13. import
14.
15. public class ImageAdapter extends BaseAdapter implements
16. /**
17. * 上下文对象的引用
18. */
19. private
20.
21. /**
22. * Image Url的数组
23. */
24. private
25.
26. /**
27. * GridView对象的应用
28. */
29. private
30.
31. /**
32. * Image 下载器
33. */
34. private
35.
36. /**
37. * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。
38. * 参考javascript:void(0)
39. */
40. private boolean isFirstEnter = true;
41.
42. /**
43. * 一屏中第一个item的位置
44. */
45. private int
46.
47. /**
48. * 一屏中所有item的个数
49. */
50. private int
51.
52.
53. public
54. this.context = context;
55. this.mGridView = mGridView;
56. this.imageThumbUrls = imageThumbUrls;
57. new
58. this);
59. }
60.
61. @Override
62. public void onScrollStateChanged(AbsListView view, int
63. //仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务
64. if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE){
65. showImage(mFirstVisibleItem, mVisibleItemCount);
66. else{
67. cancelTask();
68. }
69.
70. }
71.
72.
73. /**
74. * GridView滚动的时候调用的方法,刚开始显示GridView也会调用此方法
75. */
76. @Override
77. public void onScroll(AbsListView view, int
78. int visibleItemCount, int
79. mFirstVisibleItem = firstVisibleItem;
80. mVisibleItemCount = visibleItemCount;
81. // 因此在这里为首次进入程序开启下载任务。
82. if(isFirstEnter && visibleItemCount > 0){
83. showImage(mFirstVisibleItem, mVisibleItemCount);
84. false;
85. }
86. }
87.
88.
89. @Override
90. public int
91. return
92. }
93.
94. @Override
95. public Object getItem(int
96. return
97. }
98.
99. @Override
100. public long getItemId(int
101. return
102. }
103.
104. @Override
105. public View getView(int
106. ImageView mImageView;
107. final
108. if(convertView == null){
109. new
110. else{
111. mImageView = (ImageView) convertView;
112. }
113.
114. new GridView.LayoutParams(150, 150));
115. mImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
116.
117. //给ImageView设置Tag,这里已经是司空见惯了
118. mImageView.setTag(mImageUrl);
119.
120.
121. /*******************************去掉下面这几行试试是什么效果****************************/
122. "[^\\w]", ""));
123. if(bitmap != null){
124. mImageView.setImageBitmap(bitmap);
125. else{
126. mImageView.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_empty));
127. }
128. /**********************************************************************************/
129.
130.
131. return
132. }
133.
134. /**
135. * 显示当前屏幕的图片,先会去查找LruCache,LruCache没有就去sd卡或者手机目录查找,在没有就开启线程去下载
136. * @param firstVisibleItem
137. * @param visibleItemCount
138. */
139. private void showImage(int firstVisibleItem, int
140. null;
141. for(int
142. String mImageUrl = imageThumbUrls[i];
143. final
144. new
145.
146. @Override
147. public void
148. if(mImageView != null && bitmap != null){
149. mImageView.setImageBitmap(bitmap);
150. }
151.
152. }
153. });
154.
155. //if(bitmap != null){
156. // mImageView.setImageBitmap(bitmap);
157. //}else{
158. // mImageView.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_empty));
159. //}
160. }
161. }
162.
163. /**
164. * 取消下载任务
165. */
166. public void
167. mImageDownLoader.cancelTask();
168. }
169.
170.
171. }
  • MainActivity  里面一个GridView,然后提供一个系统菜单来删除手机上的缓存图片,直接上代码,比较简单所以里面也没有注释

1. package
2.
3. import
4. import
5. import
6. import
7. import
8. import
9.
10. public class MainActivity extends
11. private
12. private
13. private
14. private
15.
16. @Override
17. protected void
18. super.onCreate(savedInstanceState);
19. setContentView(R.layout.activity_main);
20. new FileUtils(this);
21. mGridView = (GridView) findViewById(R.id.gridView);
22. new ImageAdapter(this, mGridView, imageThumbUrls);
23. mGridView.setAdapter(mImageAdapter);
24. }
25.
26. @Override
27. protected void
28. mImageAdapter.cancelTask();
29. super.onDestroy();
30. }
31.
32. @Override
33. public boolean
34. super.onCreateOptionsMenu(menu);
35. "删除手机中图片缓存");
36. return super.onCreateOptionsMenu(menu);
37. }
38.
39. @Override
40. public boolean
41. switch
42. case 0:
43. fileUtils.deleteFile();
44. "清空缓存成功", Toast.LENGTH_SHORT).show();
45. break;
46. }
47. return super.onOptionsItemSelected(item);
48. }
49.
50. }<span style="font-family:Times New Roman;font-size:14px;">
51. </span>
  • MainActivity的布局文件

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.
7. <GridView
8. android:id="@+id/gridView"
9. android:layout_width="match_parent"
10. android:layout_height="match_parent"
11. android:stretchMode="columnWidth"
12. android:columnWidth="90dip"
13. android:verticalSpacing="10dip"
14. android:horizontalSpacing="10dip"
15. android:cacheColorHint="@android:color/transparent"
16. android:numColumns="auto_fit" >
17. </GridView>
18.
19. </RelativeLayout>
  • 因为有网络操作和对sd卡的操作,所以我们必须加上相对应的权限



1. <uses-permission android:name="android.permission.INTERNET" />
2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

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

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



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