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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android照片墙完整版,完美结合LruCache和DiskLruCache相关的知识,希望对你有一定的参考价值。

转载地址: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类,将所有相册中图片的网址都配置进去,代码如下所示:

[java] view plaincopy技术分享技术分享
 
 
  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 plaincopy技术分享技术分享
 
 
  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 plaincopy技术分享技术分享
 
 
  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 plaincopy
 
 
  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.      &nbs

以上是关于Android照片墙完整版,完美结合LruCache和DiskLruCache的主要内容,如果未能解决你的问题,请参考以下文章

Cordova fileTransfer 在 iOS 上完美运行,在 Android 上抛出错误代码 = 1

积累项目经验-完整版系统集成项目实现全过程

完美解决android软键盘监听

完美解决android软键盘监听1

调用 android 系统拍照结合 android-crop 裁剪图片

android 设置头像(PictureSelectorCircleImageView等库结合极光IM服务)