转载地址:http://blog.csdn.net/guolin_blog/article/details/34093441#comments

在上一篇文章当中,我们学习了DiskLruCache的概念和基本用法,但仅仅是掌握理论知识显然是不够的,那么本篇文章我们就来继续进阶一下,看一看在实战当中应该怎样合理使用DiskLruCache。还不熟悉DiskLruCache用法的朋友可以先去参考我的上一篇文章 Android DiskLruCache完全解析,硬盘缓存的最佳方案 。

其实,在真正的项目实战当中如果仅仅是使用硬盘缓存的话,程序是有明显短板的。而如果只使用内存缓存的话,程序当然也会有很大的缺陷。因此,一个优秀的程序必然会将内存缓存和硬盘缓存结合到一起使用,那么本篇文章我们就来看一看,如何才能将LruCache和DiskLruCache完美结合到一起。

在 Android照片墙应用实现,再多的图片也不怕崩溃 这篇文章当中,我编写了一个照片墙的应用程序,但当时只是单纯使用到了内存缓存而已,而今天我们就对这个例子进行扩展,制作一个完整版的照片墙。

那我们开始动手吧,新建一个Android项目,起名叫PhotoWallDemo,这里我使用的是Android 4.0的API。然后新建一个libcore.io包,并将DiskLruCache.java文件拷贝到这个包下,这样就把准备工作完成了。

接下来首先需要考虑的仍然是图片源的问题,简单起见,我仍然是吧所有图片都上传到了我的CSDN相册当中,然后新建一个Images类,将所有相册中图片的网址都配置进去,代码如下所示:

  1. public class Images {
  2. public final static String[] imageThumbUrls = new String[] {
  3. "http://img.my.csdn.net/uploads/201407/26/1406383299_1976.jpg",
  4. "http://img.my.csdn.net/uploads/201407/26/1406383291_6518.jpg",
  5. "http://img.my.csdn.net/uploads/201407/26/1406383291_8239.jpg",
  6. "http://img.my.csdn.net/uploads/201407/26/1406383290_9329.jpg",
  7. "http://img.my.csdn.net/uploads/201407/26/1406383290_1042.jpg",
  8. "http://img.my.csdn.net/uploads/201407/26/1406383275_3977.jpg",
  9. "http://img.my.csdn.net/uploads/201407/26/1406383265_8550.jpg",
  10. "http://img.my.csdn.net/uploads/201407/26/1406383264_3954.jpg",
  11. "http://img.my.csdn.net/uploads/201407/26/1406383264_4787.jpg",
  12. "http://img.my.csdn.net/uploads/201407/26/1406383264_8243.jpg",
  13. "http://img.my.csdn.net/uploads/201407/26/1406383248_3693.jpg",
  14. "http://img.my.csdn.net/uploads/201407/26/1406383243_5120.jpg",
  15. "http://img.my.csdn.net/uploads/201407/26/1406383242_3127.jpg",
  16. "http://img.my.csdn.net/uploads/201407/26/1406383242_9576.jpg",
  17. "http://img.my.csdn.net/uploads/201407/26/1406383242_1721.jpg",
  18. "http://img.my.csdn.net/uploads/201407/26/1406383219_5806.jpg",
  19. "http://img.my.csdn.net/uploads/201407/26/1406383214_7794.jpg",
  20. "http://img.my.csdn.net/uploads/201407/26/1406383213_4418.jpg",
  21. "http://img.my.csdn.net/uploads/201407/26/1406383213_3557.jpg",
  22. "http://img.my.csdn.net/uploads/201407/26/1406383210_8779.jpg",
  23. "http://img.my.csdn.net/uploads/201407/26/1406383172_4577.jpg",
  24. "http://img.my.csdn.net/uploads/201407/26/1406383166_3407.jpg",
  25. "http://img.my.csdn.net/uploads/201407/26/1406383166_2224.jpg",
  26. "http://img.my.csdn.net/uploads/201407/26/1406383166_7301.jpg",
  27. "http://img.my.csdn.net/uploads/201407/26/1406383165_7197.jpg",
  28. "http://img.my.csdn.net/uploads/201407/26/1406383150_8410.jpg",
  29. "http://img.my.csdn.net/uploads/201407/26/1406383131_3736.jpg",
  30. "http://img.my.csdn.net/uploads/201407/26/1406383130_5094.jpg",
  31. "http://img.my.csdn.net/uploads/201407/26/1406383130_7393.jpg",
  32. "http://img.my.csdn.net/uploads/201407/26/1406383129_8813.jpg",
  33. "http://img.my.csdn.net/uploads/201407/26/1406383100_3554.jpg",
  34. "http://img.my.csdn.net/uploads/201407/26/1406383093_7894.jpg",
  35. "http://img.my.csdn.net/uploads/201407/26/1406383092_2432.jpg",
  36. "http://img.my.csdn.net/uploads/201407/26/1406383092_3071.jpg",
  37. "http://img.my.csdn.net/uploads/201407/26/1406383091_3119.jpg",
  38. "http://img.my.csdn.net/uploads/201407/26/1406383059_6589.jpg",
  39. "http://img.my.csdn.net/uploads/201407/26/1406383059_8814.jpg",
  40. "http://img.my.csdn.net/uploads/201407/26/1406383059_2237.jpg",
  41. "http://img.my.csdn.net/uploads/201407/26/1406383058_4330.jpg",
  42. "http://img.my.csdn.net/uploads/201407/26/1406383038_3602.jpg",
  43. "http://img.my.csdn.net/uploads/201407/26/1406382942_3079.jpg",
  44. "http://img.my.csdn.net/uploads/201407/26/1406382942_8125.jpg",
  45. "http://img.my.csdn.net/uploads/201407/26/1406382942_4881.jpg",
  46. "http://img.my.csdn.net/uploads/201407/26/1406382941_4559.jpg",
  47. "http://img.my.csdn.net/uploads/201407/26/1406382941_3845.jpg",
  48. "http://img.my.csdn.net/uploads/201407/26/1406382924_8955.jpg",
  49. "http://img.my.csdn.net/uploads/201407/26/1406382923_2141.jpg",
  50. "http://img.my.csdn.net/uploads/201407/26/1406382923_8437.jpg",
  51. "http://img.my.csdn.net/uploads/201407/26/1406382922_6166.jpg",
  52. "http://img.my.csdn.net/uploads/201407/26/1406382922_4843.jpg",
  53. "http://img.my.csdn.net/uploads/201407/26/1406382905_5804.jpg",
  54. "http://img.my.csdn.net/uploads/201407/26/1406382904_3362.jpg",
  55. "http://img.my.csdn.net/uploads/201407/26/1406382904_2312.jpg",
  56. "http://img.my.csdn.net/uploads/201407/26/1406382904_4960.jpg",
  57. "http://img.my.csdn.net/uploads/201407/26/1406382900_2418.jpg",
  58. "http://img.my.csdn.net/uploads/201407/26/1406382881_4490.jpg",
  59. "http://img.my.csdn.net/uploads/201407/26/1406382881_5935.jpg",
  60. "http://img.my.csdn.net/uploads/201407/26/1406382880_3865.jpg",
  61. "http://img.my.csdn.net/uploads/201407/26/1406382880_4662.jpg",
  62. "http://img.my.csdn.net/uploads/201407/26/1406382879_2553.jpg",
  63. "http://img.my.csdn.net/uploads/201407/26/1406382862_5375.jpg",
  64. "http://img.my.csdn.net/uploads/201407/26/1406382862_1748.jpg",
  65. "http://img.my.csdn.net/uploads/201407/26/1406382861_7618.jpg",
  66. "http://img.my.csdn.net/uploads/201407/26/1406382861_8606.jpg",
  67. "http://img.my.csdn.net/uploads/201407/26/1406382861_8949.jpg",
  68. "http://img.my.csdn.net/uploads/201407/26/1406382841_9821.jpg",
  69. "http://img.my.csdn.net/uploads/201407/26/1406382840_6603.jpg",
  70. "http://img.my.csdn.net/uploads/201407/26/1406382840_2405.jpg",
  71. "http://img.my.csdn.net/uploads/201407/26/1406382840_6354.jpg",
  72. "http://img.my.csdn.net/uploads/201407/26/1406382839_5779.jpg",
  73. "http://img.my.csdn.net/uploads/201407/26/1406382810_7578.jpg",
  74. "http://img.my.csdn.net/uploads/201407/26/1406382810_2436.jpg",
  75. "http://img.my.csdn.net/uploads/201407/26/1406382809_3883.jpg",
  76. "http://img.my.csdn.net/uploads/201407/26/1406382809_6269.jpg",
  77. "http://img.my.csdn.net/uploads/201407/26/1406382808_4179.jpg",
  78. "http://img.my.csdn.net/uploads/201407/26/1406382790_8326.jpg",
  79. "http://img.my.csdn.net/uploads/201407/26/1406382789_7174.jpg",
  80. "http://img.my.csdn.net/uploads/201407/26/1406382789_5170.jpg",
  81. "http://img.my.csdn.net/uploads/201407/26/1406382789_4118.jpg",
  82. "http://img.my.csdn.net/uploads/201407/26/1406382788_9532.jpg",
  83. "http://img.my.csdn.net/uploads/201407/26/1406382767_3184.jpg",
  84. "http://img.my.csdn.net/uploads/201407/26/1406382767_4772.jpg",
  85. "http://img.my.csdn.net/uploads/201407/26/1406382766_4924.jpg",
  86. "http://img.my.csdn.net/uploads/201407/26/1406382766_5762.jpg",
  87. "http://img.my.csdn.net/uploads/201407/26/1406382765_7341.jpg"
  88. };
  89. }

设置好了图片源之后,我们需要一个GridView来展示照片墙上的每一张图片。打开或修改activity_main.xml中的代码,如下所示:

  1. <LinearLayout 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. <GridView
  6. android:id="@+id/photo_wall"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent"
  9. android:columnWidth="@dimen/image_thumbnail_size"
  10. android:gravity="center"
  11. android:horizontalSpacing="@dimen/image_thumbnail_spacing"
  12. android:numColumns="auto_fit"
  13. android:stretchMode="columnWidth"
  14. android:verticalSpacing="@dimen/image_thumbnail_spacing" >
  15. </GridView>
  16. </LinearLayout>

很简单,只是在LinearLayout中写了一个GridView而已。接着我们要定义GridView中每一个子View的布局,新建一个photo_layout.xml布局,加入如下代码:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="wrap_content"
  4. android:layout_height="wrap_content" >
  5. <ImageView
  6. android:id="@+id/photo"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent"
  9. android:layout_centerInParent="true"
  10. android:scaleType="fitXY"
  11. />
  12. </RelativeLayout>

仍然很简单,photo_layout.xml布局中只有一个ImageView控件,就是用它来显示图片的。这样我们就把所有的布局文件都写好了。

接下来新建PhotoWallAdapter做为GridView的适配器,代码如下所示:

  1. public class PhotoWallAdapter extends ArrayAdapter<String> {
  2. /**
  3. * 记录所有正在下载或等待下载的任务。
  4. */
  5. private Set<BitmapWorkerTask> taskCollection;
  6. /**
  7. * 图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。
  8. */
  9. private LruCache<String, Bitmap> mMemoryCache;
  10. /**
  11. * 图片硬盘缓存核心类。
  12. */
  13. private DiskLruCache mDiskLruCache;
  14. /**
  15. * GridView的实例
  16. */
  17. private GridView mPhotoWall;
  18. /**
  19. * 记录每个子项的高度。
  20. */
  21. private int mItemHeight = 0;
  22. public PhotoWallAdapter(Context context, int textViewResourceId, String[] objects,
  23. GridView photoWall) {
  24. super(context, textViewResourceId, objects);
  25. mPhotoWall = photoWall;
  26. taskCollection = new HashSet<BitmapWorkerTask>();
  27. // 获取应用程序最大可用内存
  28. int maxMemory = (int) Runtime.getRuntime().maxMemory();
  29. int cacheSize = maxMemory / 8;
  30. // 设置图片缓存大小为程序最大可用内存的1/8
  31. mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
  32. @Override
  33. protected int sizeOf(String key, Bitmap bitmap) {
  34. return bitmap.getByteCount();
  35. }
  36. };
  37. try {
  38. // 获取图片缓存路径
  39. File cacheDir = getDiskCacheDir(context, "thumb");
  40. if (!cacheDir.exists()) {
  41. cacheDir.mkdirs();
  42. }
  43. // 创建DiskLruCache实例,初始化缓存数据
  44. mDiskLruCache = DiskLruCache
  45. .open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
  46. } catch (IOException e) {
  47. e.printStackTrace();
  48. }
  49. }
  50. @Override
  51. public View getView(int position, View convertView, ViewGroup parent) {
  52. final String url = getItem(position);
  53. View view;
  54. if (convertView == null) {
  55. view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null);
  56. } else {
  57. view = convertView;
  58. }
  59. final ImageView imageView = (ImageView) view.findViewById(R.id.photo);
  60. if (imageView.getLayoutParams().height != mItemHeight) {
  61. imageView.getLayoutParams().height = mItemHeight;
  62. }
  63. // 给ImageView设置一个Tag,保证异步加载图片时不会乱序
  64. imageView.setTag(url);
  65. imageView.setImageResource(R.drawable.empty_photo);
  66. loadBitmaps(imageView, url);
  67. return view;
  68. }
  69. /**
  70. * 将一张图片存储到LruCache中。
  71. *
  72. * @param key
  73. *            LruCache的键,这里传入图片的URL地址。
  74. * @param bitmap
  75. *            LruCache的键,这里传入从网络上下载的Bitmap对象。
  76. */
  77. public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
  78. if (getBitmapFromMemoryCache(key) == null) {
  79. mMemoryCache.put(key, bitmap);
  80. }
  81. }
  82. /**
  83. * 从LruCache中获取一张图片,如果不存在就返回null。
  84. *
  85. * @param key
  86. *            LruCache的键,这里传入图片的URL地址。
  87. * @return 对应传入键的Bitmap对象,或者null。
  88. */
  89. public Bitmap getBitmapFromMemoryCache(String key) {
  90. return mMemoryCache.get(key);
  91. }
  92. /**
  93. * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象,
  94. * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。
  95. */
  96. public void loadBitmaps(ImageView imageView, String imageUrl) {
  97. try {
  98. Bitmap bitmap = getBitmapFromMemoryCache(imageUrl);
  99. if (bitmap == null) {
  100. BitmapWorkerTask task = new BitmapWorkerTask();
  101. taskCollection.add(task);
  102. task.execute(imageUrl);
  103. } else {
  104. if (imageView != null && bitmap != null) {
  105. imageView.setImageBitmap(bitmap);
  106. }
  107. }
  108. } catch (Exception e) {
  109. e.printStackTrace();
  110. }
  111. }
  112. /**
  113. * 取消所有正在下载或等待下载的任务。
  114. */
  115. public void cancelAllTasks() {
  116. if (taskCollection != null) {
  117. for (BitmapWorkerTask task : taskCollection) {
  118. task.cancel(false);
  119. }
  120. }
  121. }
  122. /**
  123. * 根据传入的uniqueName获取硬盘缓存的路径地址。
  124. */
  125. public File getDiskCacheDir(Context context, String uniqueName) {
  126. String cachePath;
  127. if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
  128. || !Environment.isExternalStorageRemovable()) {
  129. cachePath = context.getExternalCacheDir().getPath();
  130. } else {
  131. cachePath = context.getCacheDir().getPath();
  132. }
  133. return new File(cachePath + File.separator + uniqueName);
  134. }
  135. /**
  136. * 获取当前应用程序的版本号。
  137. */
  138. public int getAppVersion(Context context) {
  139. try {
  140. PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(),
  141. 0);
  142. return info.versionCode;
  143. } catch (NameNotFoundException e) {
  144. e.printStackTrace();
  145. }
  146. return 1;
  147. }
  148. /**
  149. * 设置item子项的高度。
  150. */
  151. public void setItemHeight(int height) {
  152. if (height == mItemHeight) {
  153. return;
  154. }
  155. mItemHeight = height;
  156. notifyDataSetChanged();
  157. }
  158. /**
  159. * 使用MD5算法对传入的key进行加密并返回。
  160. */
  161. public String hashKeyForDisk(String key) {
  162. String cacheKey;
  163. try {
  164. final MessageDigest mDigest = MessageDigest.getInstance("MD5");
  165. mDigest.update(key.getBytes());
  166. cacheKey = bytesToHexString(mDigest.digest());
  167. } catch (NoSuchAlgorithmException e) {
  168. cacheKey = String.valueOf(key.hashCode());
  169. }
  170. return cacheKey;
  171. }
  172. /**
  173. * 将缓存记录同步到journal文件中。
  174. */
  175. public void fluchCache() {
  176. if (mDiskLruCache != null) {
  177. try {
  178. mDiskLruCache.flush();
  179. } catch (IOException e) {
  180. e.printStackTrace();
  181. }
  182. }
  183. }
  184. private String bytesToHexString(byte[] bytes) {
  185. StringBuilder sb = new StringBuilder();
  186. for (int i = 0; i < bytes.length; i++) {
  187. String hex = Integer.toHexString(0xFF & bytes[i]);
  188. if (hex.length() == 1) {
  189. sb.append('0');
  190. }
  191. sb.append(hex);
  192. }
  193. return sb.toString();
  194. }
  195. /**
  196. * 异步下载图片的任务。
  197. *
  198. * @author guolin
  199. */
  200. class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
  201. /**
  202. * 图片的URL地址
  203. */
  204. private String imageUrl;
  205. @Override
  206. protected Bitmap doInBackground(String... params) {
  207. imageUrl = params[0];
  208. FileDescriptor fileDescriptor = null;
  209. FileInputStream fileInputStream = null;
  210. Snapshot snapShot = null;
  211. try {
  212. // 生成图片URL对应的key
  213. final String key = hashKeyForDisk(imageUrl);
  214. // 查找key对应的缓存
  215. snapShot = mDiskLruCache.get(key);
  216. if (snapShot == null) {
  217. // 如果没有找到对应的缓存,则准备从网络上请求数据,并写入缓存
  218. DiskLruCache.Editor editor = mDiskLruCache.edit(key);
  219. if (editor != null) {
  220. OutputStream outputStream = editor.newOutputStream(0);
  221. if (downloadUrlToStream(imageUrl, outputStream)) {
  222. editor.commit();
  223. } else {
  224. editor.abort();
  225. }
  226. }
  227. // 缓存被写入后,再次查找key对应的缓存
  228. snapShot = mDiskLruCache.get(key);
  229. }
  230. if (snapShot != null) {
  231. fileInputStream = (FileInputStream) snapShot.getInputStream(0);
  232. fileDescriptor = fileInputStream.getFD();
  233. }
  234. // 将缓存数据解析成Bitmap对象
  235. Bitmap bitmap = null;
  236. if (fileDescriptor != null) {
  237. bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
  238. }
  239. if (bitmap != null) {
  240. // 将Bitmap对象添加到内存缓存当中
  241. addBitmapToMemoryCache(params[0], bitmap);
  242. }
  243. return bitmap;
  244. } catch (IOException e) {
  245. e.printStackTrace();
  246. } finally {
  247. if (fileDescriptor == null && fileInputStream != null) {
  248. try {
  249. fileInputStream.close();
  250. } catch (IOException e) {
  251. }
  252. }
  253. }
  254. return null;
  255. }
  256. @Override
  257. protected void onPostExecute(Bitmap bitmap) {
  258. super.onPostExecute(bitmap);
  259. // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。
  260. ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl);
  261. if (imageView != null && bitmap != null) {
  262. imageView.setImageBitmap(bitmap);
  263. }
  264. taskCollection.remove(this);
  265. }
  266. /**
  267. * 建立HTTP请求,并获取Bitmap对象。
  268. *
  269. * @param imageUrl
  270. *            图片的URL地址
  271. * @return 解析后的Bitmap对象
  272. */
  273. private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
  274. HttpURLConnection urlConnection = null;
  275. BufferedOutputStream out = null;
  276. BufferedInputStream in = null;
  277. try {
  278. final URL url = new URL(urlString);
  279. urlConnection = (HttpURLConnection) url.openConnection();
  280. in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
  281. out = new BufferedOutputStream(outputStream, 8 * 1024);
  282. int b;
  283. while ((b = in.read()) != -1) {
  284. out.write(b);
  285. }
  286. return true;
  287. } catch (final IOException e) {
  288. e.printStackTrace();
  289. } finally {
  290. if (urlConnection != null) {
  291. urlConnection.disconnect();
  292. }
  293. try {
  294. if (out != null) {
  295. out.close();
  296. }
  297. if (in != null) {
  298. in.close();
  299. }
  300. } catch (final IOException e) {
  301. e.printStackTrace();
  302. }
  303. }
  304. return false;
  305. }
  306. }
  307. }

代码有点长,我们一点点进行分析。首先在PhotoWallAdapter的构造函数中,我们初始化了LruCache类,并设置了内存缓存容量为程序最大可用内存的1/8,紧接着调用了DiskLruCache的open()方法来创建实例,并设置了硬盘缓存容量为10M,这样我们就把LruCache和DiskLruCache的初始化工作完成了。

接着在getView()方法中,我们为每个ImageView设置了一个唯一的Tag,这个Tag的作用是为了后面能够准确地找回这个ImageView,不然异步加载图片会出现乱序的情况。然后在getView()方法的最后调用了loadBitmaps()方法,加载图片的具体逻辑也就是在这里执行的了。

进入到loadBitmaps()方法中可以看到,实现是调用了getBitmapFromMemoryCache()方法来从内存中获取缓存,如果获取到了则直接调用ImageView的setImageBitmap()方法将图片显示到界面上。如果内存中没有获取到,则开启一个BitmapWorkerTask任务来去异步加载图片。

那么在BitmapWorkerTask的doInBackground()方法中,我们就灵活运用了上篇文章中学习的DiskLruCache的各种用法。首先根据图片的URL生成对应的MD5 key,然后调用DiskLruCache的get()方法来获取硬盘缓存,如果没有获取到的话则从网络上请求图片并写入硬盘缓存,接着将Bitmap对象解析出来并添加到内存缓存当中,最后将这个Bitmap对象显示到界面上,这样一个完整的流程就执行完了。

那么我们再来分析一下上述流程,每次加载图片的时候都优先去内存缓存当中读取,当读取不到的时候则回去硬盘缓存中读取,而如果硬盘缓存仍然读取不到的话,就从网络上请求原始数据。不管是从硬盘缓存还是从网络获取,读取到了数据之后都应该添加到内存缓存当中,这样的话我们下次再去读取图片的时候就能迅速从内存当中读取到,而如果该图片从内存中被移除了的话,那就重复再执行一遍上述流程就可以了。

这样我们就把LruCache和DiskLruCache完美结合到一起了。接下来还需要编写MainActivity的代码,非常简单,如下所示:

  1. public class MainActivity extends Activity {
  2. /**
  3. * 用于展示照片墙的GridView
  4. */
  5. private GridView mPhotoWall;
  6. /**
  7. * GridView的适配器
  8. */
  9. private PhotoWallAdapter mAdapter;
  10. private int mImageThumbSize;
  11. private int mImageThumbSpacing;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState) {
  14. super.onCreate(savedInstanceState);
  15. setContentView(R.layout.activity_main);
  16. mImageThumbSize = getResources().getDimensionPixelSize(
  17. R.dimen.image_thumbnail_size);
  18. mImageThumbSpacing = getResources().getDimensionPixelSize(
  19. R.dimen.image_thumbnail_spacing);
  20. mPhotoWall = (GridView) findViewById(R.id.photo_wall);
  21. mAdapter = new PhotoWallAdapter(this, 0, Images.imageThumbUrls,
  22. mPhotoWall);
  23. mPhotoWall.setAdapter(mAdapter);
  24. mPhotoWall.getViewTreeObserver().addOnGlobalLayoutListener(
  25. new ViewTreeObserver.OnGlobalLayoutListener() {
  26. @Override
  27. public void onGlobalLayout() {
  28. final int numColumns = (int) Math.floor(mPhotoWall
  29. .getWidth()
  30. / (mImageThumbSize + mImageThumbSpacing));
  31. if (numColumns > 0) {
  32. int columnWidth = (mPhotoWall.getWidth() / numColumns)
  33. - mImageThumbSpacing;
  34. mAdapter.setItemHeight(columnWidth);
  35. mPhotoWall.getViewTreeObserver()
  36. .removeGlobalOnLayoutListener(this);
  37. }
  38. }
  39. });
  40. }
  41. @Override
  42. protected void onPause() {
  43. super.onPause();
  44. mAdapter.fluchCache();
  45. }
  46. @Override
  47. protected void onDestroy() {
  48. super.onDestroy();
  49. // 退出程序时结束所有的下载任务
  50. mAdapter.cancelAllTasks();
  51. }
  52. }

上述代码中,我们通过getViewTreeObserver()的方式监听View的布局事件,当布局完成以后,我们重新修改一下GridView中子View的高度,以保证子View的宽度和高度可以保持一致。

到这里还没有结束,最后还需要配置一下AndroidManifest.xml文件,并加入相应的权限,如下所示:

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2. package="com.example.photoswalldemo"
  3. android:versionCode="1"
  4. android:versionName="1.0" >
  5. <uses-sdk
  6. android:minSdkVersion="14"
  7. android:targetSdkVersion="17" />
  8. <uses-permission android:name="android.permission.INTERNET" />
  9. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  10. <application
  11. android:allowBackup="true"
  12. android:icon="@drawable/ic_launcher"
  13. android:label="@string/app_name"
  14. android:theme="@style/AppTheme" >
  15. <activity
  16. android:name="com.example.photoswalldemo.MainActivity"
  17. android:label="@string/app_name" >
  18. <intent-filter>
  19. <action android:name="android.intent.action.MAIN" />
  20. <category android:name="android.intent.category.LAUNCHER" />
  21. </intent-filter>
  22. </activity>
  23. </application>
  24. </manifest>

好了,全部代码都在这儿了,让我们来运行一下吧,效果如下图所示:

第一次从网络上请求图片的时候有点慢,但之后加载图片就会非常快了,滑动起来也很流畅。

那么我们最后再检查一下这些图片是不是已经正确缓存在指定地址了,进入 /sdcard/Android/data/<application package>/cache/thumb 这个路径,如下图所示:

可以看到,每张图片的缓存以及journal文件都在这里了,说明我们的硬盘缓存已经成功了。

好了,今天的讲解就到这里,有疑问的朋友可以在下面留言。

源码下载,请点击这里

Android照片墙完整版,完美结合LruCache和DiskLruCache的更多相关文章

  1. Android照片墙完整版,的完美结合LruCache和DiskLruCache

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/34093441 在上一篇文章其中,我们学习了DiskLruCache的概念和基本使用 ...

  2. Android照片墙完整版,完美结合 内存方案 LruCache 和 硬盘方案 DiskLruCache

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/34093441 在上一篇文章当中,我们学习了DiskLruCache的概念和基本用法 ...

  3. 将 FFmpeg 移植到 Android平台 (完整版)

    首先需要去FFmpeg的官网http://www.ffmpeg.org/去下载FFmpeg的源码,目前的版本号为FFmpeg3.3(Hilbert). 下载的文件为压缩包,解压后得到ffmpeg-3. ...

  4. Android版的菜谱客户端应用源码完整版

    Android版的菜谱客户端应用源码完整版,这个文章是从安卓教程网转载过来的,不是本人的原创,希望能够帮到大家的学习吧. <ignore_js_op> 152936qc7jdnv6vo0c ...

  5. 爱拼图游戏android源码完整版

    这个是一款爱拼图游戏源码完整版,该游戏源码比较完整的,可以支持音乐的播放在游戏的玩的过程中,还可以控制系统的声音等,可以支持多种图片的选择来进行玩的,还可以根据自己的爱好选择不同的难度来的,级别分为: ...

  6. android应用商店完整版源码

    这个是从一个安卓学习的网站上转载过来的,android应用商店完整版源码,大家可以看看一下吧. _op><ignore_js_op> <ignore_js_op>< ...

  7. Android版的疯狂猜图游戏源码完整版分享

    这个游戏源码是在安装教程网那么分享过来的,Android版的疯狂猜图游戏源码完整版分享,也是本人之前很早以前发的一款游戏源码的,大家如果想了解一下,可以看看吧,不说多了,上一个图先吧.   > ...

  8. Android开发之ExpandableListView扩展(BaseExpandableListAdapter的使用)(完整版)

    Android开发之ExpandableListView扩展(BaseExpandableListAdapter的使用)(完整版)

  9. Android学习之基础知识十三 — 四大组件之服务详解第二讲(完整版的下载示例)

    上一讲学习了很多关于服务的使用技巧,但是当在真正的项目里需要用到服务的时候,可能还会有一些棘手的问题让你不知所措.接下来就来综合运用一下,尝试实现一下在服务中经常会使用到的功能——下载. 在这一讲我们 ...

随机推荐

  1. HDU 4739 Zhuge Liang's Mines (状态压缩+背包DP)

    题意 给定平面直角坐标系内的N(N <= 20)个点,每四个点构成一个正方形可以消去,问最多可以消去几个点. 思路 比赛的时候暴力dfs+O(n^4)枚举写过了--无意间看到有题解用状压DP(这 ...

  2. IOS UI-控制器的生命周期

    一.控制器的生命周期 代码 @interface NJOneViewController () @property (nonatomic, strong) NSArray *foods; @end @ ...

  3. 添加resx文件过程笔记

    为了测试资源文件,今天上午耗掉了.终于实现了,原来是要按照它自动建文件夹,不可以手动建.下图 见其他语言的时候一定要:Mytest.zh-CN.resx 这样,加上后缀名字 代码中获取资源 http: ...

  4. CF910A

    题解: 简单dp dp[i]=min(dp[i-j])+1; 代码: #include<bits/stdc++.h> using namespace std; ; int n,m,dp[N ...

  5. session、cookie、viewstate

    session的用法 定义:保存在服务器内存的数据,sesson 只应该应用在需要跨页面且与每个访问用户相关的变量和对象存储上,session在默认情况下20分钟就过期,在页面之中最好不要过多使用,因 ...

  6. LeetCode OJ:Find Median from Data Stream(找数据流的中数)

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no ...

  7. CSS3 文本超出后显示省略号...

    纯用CSS实现,主要采用代码 overflow:hidden; text-overflow:ellipsis;//这是让文本溢出后,显示成省略号. white-space:nowrap;//禁止自动换 ...

  8. mifi随身wifi选购

    一款优秀的随身wifi不光要信号好,更要电量足 ,网速快.影响这个三个问题的主要因素就是cpu.so咱们从cpu的角度来分析下mifi 机器型号(cpu型号) TP 961 52000 (MDM962 ...

  9. Java之引用类型分析(SoftReference/WeakReference/PhantomReference)

    引言: 即使对于Java的很多老鸟来说,如果忽然问他引用的类型,大概率是一脸茫然,不知所措的-.Java中的引用还分类型,神马情况??? 本文将针对这些类型进行分析,帮助您一文知所有类型. Java的 ...

  10. 打开U盘时出现“使用驱动器 X:中的光盘之前需要将其格式化。”字样

    今天打开U盘时出现如下情况,吓死宝宝了,格式化!!!里面还有好多数据嘞,幸好最终找到方法解决了这个问题. 解决方法: 1.下载DiskGenius软件 2.工具->搜索已丢失分区(重建分区):不 ...