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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

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

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

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

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

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

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

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

[html]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  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.   
  6.     <GridView  
  7.         android:id="@+id/photo_wall"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="match_parent"  
  10.         android:columnWidth="@dimen/image_thumbnail_size"  
  11.         android:gravity="center"  
  12.         android:horizontalSpacing="@dimen/image_thumbnail_spacing"  
  13.         android:numColumns="auto_fit"  
  14.         android:stretchMode="columnWidth"  
  15.         android:verticalSpacing="@dimen/image_thumbnail_spacing" >  
  16.     </GridView>  
  17.   
  18. </LinearLayout>  

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

[html]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  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.   
  6.     <ImageView   
  7.         android:id="@+id/photo"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="match_parent"  
  10.         android:layout_centerInParent="true"  
  11.         android:scaleType="fitXY"  
  12.         />  
  13.   
  14. </RelativeLayout>  

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

 

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

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

代码有点长,我们一点点进行分析。首先在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的代码,非常简单,如下所示:

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

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

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

[html]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  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.   
  6.     <uses-sdk  
  7.         android:minSdkVersion="14"  
  8.         android:targetSdkVersion="17" />  
  9.   
  10.     <uses-permission android:name="android.permission.INTERNET" />  
  11.     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
  12.   
  13.     <application  
  14.         android:allowBackup="true"  
  15.         android:icon="@drawable/ic_launcher"  
  16.         android:label="@string/app_name"  
  17.         android:theme="@style/AppTheme" >  
  18.         <activity  
  19.             android:name="com.example.photoswalldemo.MainActivity"  
  20.             android:label="@string/app_name" >  
  21.             <intent-filter>  
  22.                 <action android:name="android.intent.action.MAIN" />  
  23.                 <category android:name="android.intent.category.LAUNCHER" />  
  24.             </intent-filter>  
  25.         </activity>  
  26.     </application>  
  27.   
  28. </manifest>  

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

 

 

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

 

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

 

 

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

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



本文转自农夫山泉别墅博客园博客,原文链接:http://www.cnblogs.com/yaowen/p/6348068.html,如需转载请自行联系原作者

相关文章
|
7月前
|
缓存 运维 Shell
幻兽帕鲁爆内存优化方案
最近有很多小伙伴反馈说4C16G的服务器玩时间久了经常出现内存过满自动重启的情况,现在总结下linux和windows系统下怎么进行优化。
|
1月前
|
存储 架构师 Java
内存溢出原因与解决方案(4大主流方案详解)
本文详解内存溢出(OOM)的原因及解决方案。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
内存溢出原因与解决方案(4大主流方案详解)
|
3月前
|
开发框架 Dart 前端开发
Android 跨平台方案对比之Flutter 和 React Native
本文对比了 Flutter 和 React Native 这两个跨平台移动应用开发框架。Flutter 使用 Dart 语言,提供接近原生的性能和丰富的组件库;React Native 则基于 JavaScript,具备庞大的社区支持和灵活性。两者各有优势,选择时需考虑团队技能和项目需求。
415 8
|
3月前
|
Web App开发 网络协议 Android开发
Android平台一对一音视频通话方案大比拼:WebRTC VS RTMP VS RTSP,谁才是王者?
【9月更文挑战第4天】本文详细对比了在Android平台上实现一对一音视频通话时常用的WebRTC、RTMP及RTSP三种技术方案。从技术原理、性能表现与开发难度等方面进行了深入分析,并提供了示例代码。WebRTC适合追求低延迟和高质量的场景,但开发成本较高;RTMP和RTSP则在简化开发流程的同时仍能保持较好的传输效果,适用于不同需求的应用场景。
187 1
|
4月前
|
存储 安全 API
Android经典实战之存储方案对比:SharedPreferences vs MMKV vs DataStore
本文介绍了 Android 开发中常用的键值对存储方案,包括 SharedPreferences、MMKV 和 DataStore,并对比了它们在性能、并发处理、易用性和稳定性上的特点。通过实际代码示例,帮助开发者根据项目需求选择最适合的存储方案,提升应用性能和用户体验。
135 1
|
4月前
|
Android开发 C++ 开发者
Android经典实战之跨平台开发方案:Kotlin Multiplatform vs Flutter
本文对比了Kotlin Multiplatform与Flutter两大跨平台开发框架,从技术特性、性能、开发效率、UI体验、可扩展性及适用场景等维度进行了详尽分析,帮助开发者根据项目需求和技术背景选择最优方案。
175 2
|
4月前
|
Web App开发 网络协议 Android开发
### 惊天对决!Android平台一对一音视频通话方案大比拼:WebRTC VS RTMP VS RTSP,谁才是王者?
【8月更文挑战第14天】随着移动互联网的发展,实时音视频通信已成为移动应用的关键部分。本文对比分析了Android平台上WebRTC、RTMP与RTSP三种主流技术方案。WebRTC提供端到端加密与直接数据传输,适于高质量低延迟通信;RTMP适用于直播场景,但需服务器中转;RTSP支持实时流播放,但在复杂网络下稳定性不及WebRTC。三种方案各有优劣,WebRTC功能强大但集成复杂,RTMP和RTSP实现较简单但需额外编码支持。本文还提供了示例代码以帮助开发者更好地理解和应用这些技术。
158 0
|
6月前
|
存储 Python
Python成员属性的内存特性与底层内存优化方案
这篇博客主要分享一下python成员属性的内存特性,也就是python底层节约内存的优化方案
|
6月前
|
缓存 Java Linux
Android 匿名内存深入分析
Android 匿名内存深入分析
45 0
|
7月前
|
存储 缓存 算法
深入理解操作系统内存管理:分页系统的优势与挑战构建高效Android应用:探究Kotlin协程的优势与实践
【5月更文挑战第27天】 在现代计算机系统中,内存管理是操作系统的核心功能之一。分页系统作为一种内存管理技术,通过将物理内存划分为固定大小的单元——页面,为每个运行的程序提供独立的虚拟地址空间。这种机制不仅提高了内存的使用效率,还为多任务环境提供了必要的隔离性。然而,分页系统的实现也带来了一系列的挑战,包括页面置换算法的选择、内存抖动问题以及TLB(Translation Lookaside Buffer)的管理等。本文旨在探讨分页系统的原理、优势及其面临的挑战,并通过分析现有解决方案,提出可能的改进措施。