转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/34093441

在上一篇文章当中,我们学习了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#comments 在上一篇文章当中,我们学习了DiskLruCache的概 ...

  3. Cache【硬盘缓存工具类(包含内存缓存LruCache和磁盘缓存DiskLruCache)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 内存缓存LruCache和磁盘缓存DiskLruCache的封装类,主要用于图片缓存. 效果图 代码分析 内存缓存LruCache和 ...

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

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

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

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

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

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

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

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

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

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

  9. Android消息推送完美方案[转]

    转自 Android消息推送完美方案 推送功能在手机应用开发中越来越重要,已经成为手机开发的必须.在Android应用开发中,由于众所周知的原因,Android消息推送我们不得不大费周折.本文就是用来 ...

随机推荐

  1. sicily 1031 Campus(图算法)

    Description At present, Zhongshan University has 4 campuses with a total area of 6.17 square kilomet ...

  2. SharePoint UserProfileService 接口列表 注解

    Remove Leader 除去领袖 Add Leader  加领袖 Get leaders  获得管理员 Get Profile Scherna Get Profile Scherna Names ...

  3. Django分页和查询参数的问题

    查询是通过get的方式,之前没有分页之前,url是这样的: http://hostname/search?query=port%3A8080 那么我的想法是如果分页了. 1,不带page参数了.nex ...

  4. Spring 注解拦截器使用详解

    Spring mvc拦截器 平时用到的拦截器通常都是xml的配置方式.今天就特地研究了一下注解方式的拦截器. 配置Spring环境这里就不做详细介绍.本文主要介绍在Spring下,基于注解方式的拦截器 ...

  5. PostgreSQL Replication之第九章 与pgpool一起工作(1)

    在前面的章节中,我们已经能够深入地理解了pgbouncer,同时也学会了如何使用它来尽可能地优化复制设置.在本章我们将了解一个经常被称作与pgbouncer相对应的工具.尽管pgpool的思想与pgb ...

  6. PostgreSQL Replication之第八章 与pgbouncer一起工作(4)

    8.4 提升性能 从一开始考虑pgbouncer的时候,性能就是一个关键的因素.为了确保高性能,有些问题必须认真对待.首先,确保参与您设置的所有节点相互之间的距离较近.这对于降低网络往返时间有很多的帮 ...

  7. PostgreSQL Replication之第八章 与pgbouncer一起工作(3)

    8.3 配置您的第一个pgbouncer设置 一旦我们已经完成了pbouncer的编译与安装,我们可以容易地启动它.要做到这一点,我们已经在一个本地实例(p0和p1) 建立了两个数据库.在本例中,执行 ...

  8. SQL Server存储ntext截断问题

    SQL Server存储ntext截断问题   最近遇到一个问题:将大文本存储到数据库的时候,查询出来的文本却被截断了. 最后百度发现,作者提出 sql server management studi ...

  9. [Bug]Python3.x SyntaxError: 'ascii' codec can't decode byte 0xe4 in position

    安装arch后就没再用python了 昨天管服务器的大佬在跑贝叶斯分类器的时候发现正确率有问题 我赶紧去做优化,然后就有这样的报错 Python 3.6.4 (default, Jan 5 2018, ...

  10. CF 986C AND Graph(建模+DFS)

    #include<stdio.h> ],v[]; ],n,al; void dfs(int x){ if(v[x])return; v[x]=; if(ex[x])dfs(al^x); ; ...