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

简介:

在上一篇文章当中,我们学习了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,如需转载请自行联系原作者

相关文章
|
22天前
|
编解码 算法 Java
构建高效的Android应用:内存优化策略详解
随着智能手机在日常生活和工作中的普及,用户对移动应用的性能要求越来越高。特别是对于Android开发者来说,理解并实践内存优化是提升应用程序性能的关键步骤。本文将深入探讨针对Android平台的内存管理机制,并提供一系列实用的内存优化技巧,以帮助开发者减少内存消耗,避免常见的内存泄漏问题,并确保应用的流畅运行。
|
3月前
|
缓存 运维 Shell
幻兽帕鲁爆内存优化方案
最近有很多小伙伴反馈说4C16G的服务器玩时间久了经常出现内存过满自动重启的情况,现在总结下linux和windows系统下怎么进行优化。
1288 1
|
7月前
|
缓存 Java Shell
Android 内存泄露,怎样查找,怎么产生的内存泄露?
Android 内存泄露,怎样查找,怎么产生的内存泄露?
50 0
|
23天前
|
缓存 移动开发 Java
构建高效Android应用:内存优化实战指南
在移动开发领域,性能优化是提升用户体验的关键因素之一。特别是对于Android应用而言,由于设备和版本的多样性,内存管理成为开发者面临的一大挑战。本文将深入探讨Android内存优化的策略和技术,包括内存泄漏的诊断与解决、合理的数据结构选择、以及有效的资源释放机制。通过实际案例分析,我们旨在为开发者提供一套实用的内存优化工具和方法,以构建更加流畅和高效的Android应用。
|
26天前
|
监控 Java Android开发
构建高效Android应用:从内存管理到性能优化
【2月更文挑战第30天】 在移动开发领域,打造一个流畅且响应迅速的Android应用是每个开发者追求的目标。本文将深入探讨如何通过有效的内存管理和细致的性能调优来提升应用效率。我们将从分析内存泄露的根本原因出发,讨论垃圾回收机制,并探索多种内存优化策略。接着,文中将介绍多线程编程的最佳实践和UI渲染的关键技巧。最后,我们将通过一系列实用的性能测试工具和方法,帮助开发者监控、定位并解决性能瓶颈。这些技术的综合运用,将指导读者构建出更快速、更稳定、用户体验更佳的Android应用。
|
1月前
|
传感器 缓存 Android开发
构建高效的Android应用:从内存优化到电池寿命
【2月更文挑战第23天】在移动开发领域,性能优化是一个持续的挑战。特别是对于Android应用来说,由于设备多样性和碎片化问题,开发者需要采取一系列策略来保证应用的流畅运行。本文深入探讨了Android应用的性能优化,包括内存管理、电池使用效率和UI渲染。我们将提供实用的技巧和最佳实践,帮助开发者构建更加高效、响应迅速的应用,从而改善用户体验并延长设备电池寿命。
13 1
|
1月前
|
监控 Java Android开发
构建高效Android应用:从内存优化到电池寿命
【2月更文挑战第18天】在移动设备的生态系统中,资源管理是确保用户满意度的关键。特别是对于Android开发者来说,优化应用的内存使用和延长电池寿命是提升用户体验的重要方面。本文将深入探讨Android平台上的内存管理机制,提供实用的代码级优化技巧,并讨论如何通过调整应用行为来减少能量消耗,最终目标是为开发者提供一套全面的技术策略,以打造更加高效、响应迅速且电池友好的Android应用。
28 5
|
7月前
|
算法 Java Android开发
Android rxjava和LiveData中的内存泄漏
Android rxjava和LiveData中的内存泄漏
116 0
|
3月前
|
Java 数据库连接 程序员
Android 性能优化: 什么是内存泄漏?如何在Android中避免内存泄漏?
Android 性能优化: 什么是内存泄漏?如何在Android中避免内存泄漏?
65 2
|
4月前
|
运维 Linux
Linux 查询 OS、CPU、内存、硬盘信息
Linux 查询 OS、CPU、内存、硬盘信息
88 0