LruCache为GridView异步加载大量网络图片

时间:2023-03-09 21:47:20
LruCache为GridView异步加载大量网络图片

MainActivity如下:

  1. import android.os.Bundle;
  2. import android.widget.GridView;
  3. import android.app.Activity;
  4. /**
  5. * Demo描述:
  6. * 在GridView中采用LruCache异步加载大量图片,避免OOM
  7. *
  8. * 学习资料:
  9. * http://blog.csdn.net/guolin_blog/article/details/9526203
  10. * Thank you very much
  11. */
  12. public class MainActivity extends Activity {
  13. private GridView mGridView;
  14. private GridViewAdapter mGridViewAdapter;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState) {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.main);
  19. init();
  20. }
  21. private void init(){
  22. mGridView = (GridView) findViewById(R.id.gridView);
  23. mGridViewAdapter = new GridViewAdapter(this, 0, ImagesUrl.Urls, mGridView);
  24. mGridView.setAdapter(mGridViewAdapter);
  25. }
  26. //取消所有的下载任务
  27. @Override
  28. protected void onDestroy() {
  29. super.onDestroy();
  30. mGridViewAdapter.cancelAllTasks();
  31. }

GridViewAdapter如下:

  1. import java.net.HttpURLConnection;
  2. import java.net.URL;
  3. import java.util.HashSet;
  4. import android.annotation.SuppressLint;
  5. import android.content.Context;
  6. import android.graphics.Bitmap;
  7. import android.graphics.BitmapFactory;
  8. import android.os.AsyncTask;
  9. import android.support.v4.util.LruCache;
  10. import android.view.LayoutInflater;
  11. import android.view.View;
  12. import android.view.ViewGroup;
  13. import android.widget.AbsListView;
  14. import android.widget.AbsListView.OnScrollListener;
  15. import android.widget.ArrayAdapter;
  16. import android.widget.GridView;
  17. import android.widget.ImageView;
  18. /**
  19. * LruCache的流程分析:
  20. * 我们从第一次进入应用的情况下开始
  21. * 1 依据图片的Url从LruCache缓存中取图片.
  22. *   若图片存在缓存中,则显示该图片;否则显示默认图片
  23. * 2 因为是第一次进入该界面所以会执行:
  24. *   loadBitmaps(firstVisibleItem, visibleItemCount);
  25. *   我们从loadBitmaps()方法作为切入点,继续往下梳理
  26. * 3 尝试从LruCache缓存中取图片.如果在显示即可,否则进入4
  27. * 4 开启一个异步任务下载图片.下载完成后显示图片,并且将
  28. *   该图片存入LruCache缓存中
  29. *
  30. * 在停止滑动时,会调用loadBitmaps(firstVisibleItem, visibleItemCount)
  31. * 情况与上类似
  32. */
  33. @SuppressLint("NewApi")
  34. public class GridViewAdapter extends ArrayAdapter<String> {
  35. private GridView mGridView;
  36. //图片缓存类
  37. private LruCache<String, Bitmap> mLruCache;
  38. //记录所有正在下载或等待下载的任务
  39. private HashSet<DownloadBitmapAsyncTask> mDownloadBitmapAsyncTaskHashSet;
  40. //GridView中可见的第一张图片的下标
  41. private int mFirstVisibleItem;
  42. //GridView中可见的图片的数量
  43. private int mVisibleItemCount;
  44. //记录是否是第一次进入该界面
  45. private boolean isFirstEnterThisActivity = true;
  46. public GridViewAdapter(Context context, int textViewResourceId,String[] objects, GridView gridView) {
  47. super(context, textViewResourceId, objects);
  48. mGridView = gridView;
  49. mGridView.setOnScrollListener(new ScrollListenerImpl());
  50. mDownloadBitmapAsyncTaskHashSet = new HashSet<DownloadBitmapAsyncTask>();
  51. // 获取应用程序最大可用内存
  52. int maxMemory = (int) Runtime.getRuntime().maxMemory();
  53. // 设置图片缓存大小为maxMemory的1/6
  54. int cacheSize = maxMemory/6;
  55. mLruCache = new LruCache<String, Bitmap>(cacheSize) {
  56. @Override
  57. protected int sizeOf(String key, Bitmap bitmap) {
  58. return bitmap.getByteCount();
  59. }
  60. };
  61. }
  62. @Override
  63. public View getView(int position, View convertView, ViewGroup parent) {
  64. String url = getItem(position);
  65. View view;
  66. if (convertView == null) {
  67. view = LayoutInflater.from(getContext()).inflate(R.layout.gridview_item, null);
  68. } else {
  69. view = convertView;
  70. }
  71. ImageView imageView = (ImageView) view.findViewById(R.id.imageView);
  72. //为该ImageView设置一个Tag,防止图片错位
  73. imageView.setTag(url);
  74. //为该ImageView设置显示的图片
  75. setImageForImageView(url, imageView);
  76. return view;
  77. }
  78. /**
  79. * 为ImageView设置图片(Image)
  80. * 1 从缓存中获取图片
  81. * 2 若图片不在缓存中则为其设置默认图片
  82. */
  83. private void setImageForImageView(String imageUrl, ImageView imageView) {
  84. Bitmap bitmap = getBitmapFromLruCache(imageUrl);
  85. if (bitmap != null) {
  86. imageView.setImageBitmap(bitmap);
  87. } else {
  88. imageView.setImageResource(R.drawable.default_image);
  89. }
  90. }
  91. /**
  92. * 将图片存储到LruCache
  93. */
  94. public void addBitmapToLruCache(String key, Bitmap bitmap) {
  95. if (getBitmapFromLruCache(key) == null) {
  96. mLruCache.put(key, bitmap);
  97. }
  98. }
  99. /**
  100. * 从LruCache缓存获取图片
  101. */
  102. public Bitmap getBitmapFromLruCache(String key) {
  103. return mLruCache.get(key);
  104. }
  105. /**
  106. * 为GridView的item加载图片
  107. *
  108. * @param firstVisibleItem
  109. * GridView中可见的第一张图片的下标
  110. *
  111. * @param visibleItemCount
  112. * GridView中可见的图片的数量
  113. *
  114. */
  115. private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
  116. try {
  117. for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
  118. String imageUrl = ImagesUrl.Urls[i];
  119. Bitmap bitmap = getBitmapFromLruCache(imageUrl);
  120. if (bitmap == null) {
  121. DownloadBitmapAsyncTask downloadBitmapAsyncTask = new DownloadBitmapAsyncTask();
  122. mDownloadBitmapAsyncTaskHashSet.add(downloadBitmapAsyncTask);
  123. downloadBitmapAsyncTask.execute(imageUrl);
  124. } else {
  125. //依据Tag找到对应的ImageView显示图片
  126. ImageView imageView = (ImageView) mGridView.findViewWithTag(imageUrl);
  127. if (imageView != null && bitmap != null) {
  128. imageView.setImageBitmap(bitmap);
  129. }
  130. }
  131. }
  132. } catch (Exception e) {
  133. e.printStackTrace();
  134. }
  135. }
  136. /**
  137. * 取消所有正在下载或等待下载的任务
  138. */
  139. public void cancelAllTasks() {
  140. if (mDownloadBitmapAsyncTaskHashSet != null) {
  141. for (DownloadBitmapAsyncTask task : mDownloadBitmapAsyncTaskHashSet) {
  142. task.cancel(false);
  143. }
  144. }
  145. }
  146. private class ScrollListenerImpl implements OnScrollListener{
  147. /**
  148. *
  149. * 我们的本意是通过onScrollStateChanged获知:每次GridView停止滑动时加载图片
  150. * 但是存在一个特殊情况:
  151. * 当第一次入应用的时候,此时并没有滑动屏幕的操作即不会调用onScrollStateChanged,但应该加载图片.
  152. * 所以在此处做一个特殊的处理.
  153. * 即代码:
  154. * if (isFirstEnterThisActivity && visibleItemCount > 0) {
  155. *      loadBitmaps(firstVisibleItem, visibleItemCount);
  156. *      isFirstEnterThisActivity = false;
  157. *    }
  158. *
  159. * ------------------------------------------------------------
  160. *
  161. * 其余的都是正常情况.
  162. * 所以我们需要不断保存:firstVisibleItem和visibleItemCount
  163. * 从而便于中在onScrollStateChanged()判断当停止滑动时加载图片
  164. *
  165. */
  166. @Override
  167. public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
  168. mFirstVisibleItem = firstVisibleItem;
  169. mVisibleItemCount = visibleItemCount;
  170. if (isFirstEnterThisActivity && visibleItemCount > 0) {
  171. loadBitmaps(firstVisibleItem, visibleItemCount);
  172. isFirstEnterThisActivity = false;
  173. }
  174. }
  175. /**
  176. *  GridView停止滑动时下载图片
  177. *  其余情况下取消所有正在下载或者等待下载的任务
  178. */
  179. @Override
  180. public void onScrollStateChanged(AbsListView view, int scrollState) {
  181. if (scrollState == SCROLL_STATE_IDLE) {
  182. loadBitmaps(mFirstVisibleItem, mVisibleItemCount);
  183. } else {
  184. cancelAllTasks();
  185. }
  186. }
  187. }
  188. /**
  189. * 下载图片的异步任务
  190. */
  191. class DownloadBitmapAsyncTask extends AsyncTask<String, Void, Bitmap> {
  192. private String imageUrl;
  193. @Override
  194. protected Bitmap doInBackground(String... params) {
  195. imageUrl = params[0];
  196. Bitmap bitmap = downloadBitmap(params[0]);
  197. if (bitmap != null) {
  198. //下载完后,将其缓存到LrcCache
  199. addBitmapToLruCache(params[0], bitmap);
  200. }
  201. return bitmap;
  202. }
  203. @Override
  204. protected void onPostExecute(Bitmap bitmap) {
  205. super.onPostExecute(bitmap);
  206. //下载完成后,找到其对应的ImageView显示图片
  207. ImageView imageView = (ImageView) mGridView.findViewWithTag(imageUrl);
  208. if (imageView != null && bitmap != null) {
  209. imageView.setImageBitmap(bitmap);
  210. }
  211. mDownloadBitmapAsyncTaskHashSet.remove(this);
  212. }
  213. }
  214. // 获取Bitmap
  215. private Bitmap downloadBitmap(String imageUrl) {
  216. Bitmap bitmap = null;
  217. HttpURLConnection httpURLConnection = null;
  218. try {
  219. URL url = new URL(imageUrl);
  220. httpURLConnection = (HttpURLConnection) url.openConnection();
  221. httpURLConnection.setConnectTimeout(5 * 1000);
  222. httpURLConnection.setReadTimeout(10 * 1000);
  223. httpURLConnection.setDoInput(true);
  224. httpURLConnection.setDoOutput(true);
  225. bitmap = BitmapFactory.decodeStream(httpURLConnection.getInputStream());
  226. } catch (Exception e) {
  227. e.printStackTrace();
  228. } finally {
  229. if (httpURLConnection != null) {
  230. httpURLConnection.disconnect();
  231. }
  232. }
  233. return bitmap;
  234. }
  235. }

ImagesUrl如下:

  1. public class ImagesUrl {
  2. public final static String[] Urls = new String[] {
  3. "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s160-c/A%252520Photographer.jpg",
  4. "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s160-c/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",
  5. "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s160-c/Another%252520Rockaway%252520Sunset.jpg",
  6. "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s160-c/Antelope%252520Butte.jpg",
  7. "https://lh6.googleusercontent.com/-8HO-4vIFnlw/URquZnsFgtI/AAAAAAAAAbs/WT8jViTF7vw/s160-c/Antelope%252520Hallway.jpg",
  8. "https://lh4.googleusercontent.com/-WIuWgVcU3Qw/URqubRVcj4I/AAAAAAAAAbs/YvbwgGjwdIQ/s160-c/Antelope%252520Walls.jpg",
  9. "https://lh6.googleusercontent.com/-UBmLbPELvoQ/URqucCdv0kI/AAAAAAAAAbs/IdNhr2VQoQs/s160-c/Apre%2525CC%252580s%252520la%252520Pluie.jpg"};