Chinaunix首页 | 论坛 | 博客
  • 博客访问: 387790
  • 博文数量: 214
  • 博客积分: 770
  • 博客等级: 军士长
  • 技术积分: 1969
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-08 01:22
文章分类

全部博文(214)

文章存档

2013年(110)

2012年(104)

我的朋友

分类: Android平台

2013-07-31 15:18:43

本文链接地址:
如果每次加载同一张图片都要从网络获取,那代价实在太大了。所以同一张图片只要从网络获取一次就够了,然后在本地缓存起来,之后加载同一张图片时就从缓存中加载就可以了。从内存缓存读取图片是最快的,但是因为内存容量有限,所以最好再加上文件缓存。文件缓存空间也不是无限大的,容量越大读取效率越低,因此可以设置一个限定大小比如10M,或者限定保存时间比如一天。

因此,加载图片的流程应该是:

1、先从内存缓存中获取,取到则返回,取不到则进行下一步;

2、从文件缓存中获取,取到则返回并更新到内存缓存,取不到则进行下一步;

3、从网络下载图片,并更新到内存缓存和文件缓存。

 

接下来看内存缓存类:ImageMemoryCache



点击(此处)折叠或打开

  1. public class ImageMemoryCache {
  2.     /**
  3.      * 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。
  4.      * 硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。
  5.      */
  6.     private static final int SOFT_CACHE_SIZE = 15; //软引用缓存容量
  7.     private static LruCache<String, Bitmap> mLruCache; //硬引用缓存
  8.     private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; //软引用缓存
  9.                                                                                           
  10.     public ImageMemoryCache(Context context) {
  11.         int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
  12.         int cacheSize = 1024 * 1024 * memClass / 4; //硬引用缓存容量,为系统可用内存的1/4
  13.         mLruCache = new LruCache<String, Bitmap>(cacheSize) {
  14.             @Override
  15.             protected int sizeOf(String key, Bitmap value) {
  16.                 if (value != null)
  17.                     return value.getRowBytes() * value.getHeight();
  18.                 else
  19.                     return 0;
  20.             }
  21.                                                                                           
  22.             @Override
  23.             protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
  24.                 if (oldValue != null)
  25.                     // 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存
  26.                     mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
  27.             }
  28.         };
  29.         mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {
  30.             private static final long serialVersionUID = 6040103833179403725L;
  31.             @Override
  32.             protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
  33.                 if (size() > SOFT_CACHE_SIZE){
  34.                     return true;
  35.                 }
  36.                 return false;
  37.             }
  38.         };
  39.     }
  40.                                                                                   
  41.     /**
  42.      * 从缓存中获取图片
  43.      */
  44.     public Bitmap getBitmapFromCache(String url) {
  45.         Bitmap bitmap;
  46.         //先从硬引用缓存中获取
  47.         synchronized (mLruCache) {
  48.             bitmap = mLruCache.get(url);
  49.             if (bitmap != null) {
  50.                 //如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除
  51.                 mLruCache.remove(url);
  52.                 mLruCache.put(url, bitmap);
  53.                 return bitmap;
  54.             }
  55.         }
  56.         //如果硬引用缓存中找不到,到软引用缓存中找
  57.         synchronized (mSoftCache) {
  58.             SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
  59.             if (bitmapReference != null) {
  60.                 bitmap = bitmapReference.get();
  61.                 if (bitmap != null) {
  62.                     //将图片移回硬缓存
  63.                     mLruCache.put(url, bitmap);
  64.                     mSoftCache.remove(url);
  65.                     return bitmap;
  66.                 } else {
  67.                     mSoftCache.remove(url);
  68.                 }
  69.             }
  70.         }
  71.         return null;
  72.     }
  73.                                                                                   
  74.     /**
  75.      * 添加图片到缓存
  76.      */
  77.     public void addBitmapToCache(String url, Bitmap bitmap) {
  78.         if (bitmap != null) {
  79.             synchronized (mLruCache) {
  80.                 mLruCache.put(url, bitmap);
  81.             }
  82.         }
  83.     }
  84.                                                                                   
  85.     public void clearCache() {
  86.         mSoftCache.clear();
  87.     }
  88. }



文件缓存类:ImageFileCache

点击(此处)折叠或打开

  1. public class ImageFileCache {
  2.     private static final String CACHDIR = "ImgCach";
  3.     private static final String WHOLESALE_CONV = ".cach";
  4.                                                             
  5.     private static final int MB = 1024*1024;
  6.     private static final int CACHE_SIZE = 10;
  7.     private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;
  8.                                                                 
  9.     public ImageFileCache() {
  10.         //清理文件缓存
  11.         removeCache(getDirectory());
  12.     }
  13.                                                                 
  14.     /** 从缓存中获取图片 **/
  15.     public Bitmap getImage(final String url) {
  16.         final String path = getDirectory() + "/" + convertUrlToFileName(url);
  17.         File file = new File(path);
  18.         if (file.exists()) {
  19.             Bitmap bmp = BitmapFactory.decodeFile(path);
  20.             if (bmp == null) {
  21.                 file.delete();
  22.             } else {
  23.                 updateFileTime(path);
  24.                 return bmp;
  25.             }
  26.         }
  27.         return null;
  28.     }
  29.                                                                 
  30.     /** 将图片存入文件缓存 **/
  31.     public void saveBitmap(Bitmap bm, String url) {
  32.         if (bm == null) {
  33.             return;
  34.         }
  35.         //判断sdcard上的空间
  36.         if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
  37.             //SD空间不足
  38.             return;
  39.         }
  40.         String filename = convertUrlToFileName(url);
  41.         String dir = getDirectory();
  42.         File dirFile = new File(dir);
  43.         if (!dirFile.exists())
  44.             dirFile.mkdirs();
  45.         File file = new File(dir +"/" + filename);
  46.         try {
  47.             file.createNewFile();
  48.             OutputStream outStream = new FileOutputStream(file);
  49.             bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
  50.             outStream.flush();
  51.             outStream.close();
  52.         } catch (FileNotFoundException e) {
  53.             Log.w("ImageFileCache", "FileNotFoundException");
  54.         } catch (IOException e) {
  55.             Log.w("ImageFileCache", "IOException");
  56.         }
  57.     }
  58.                                                                 
  59.     /**
  60.      * 计算存储目录下的文件大小,
  61.      * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
  62.      * 那么删除40%最近没有被使用的文件
  63.      */
  64.     private boolean removeCache(String dirPath) {
  65.         File dir = new File(dirPath);
  66.         File[] files = dir.listFiles();
  67.         if (files == null) {
  68.             return true;
  69.         }
  70.         if (!android.os.Environment.getExternalStorageState().equals(
  71.                 android.os.Environment.MEDIA_MOUNTED)) {
  72.             return false;
  73.         }
  74.                                                             
  75.         int dirSize = 0;
  76.         for (int i = 0; i < files.length; i++) {
  77.             if (files[i].getName().contains(WHOLESALE_CONV)) {
  78.                 dirSize += files[i].length();
  79.             }
  80.         }
  81.                                                             
  82.         if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
  83.             int removeFactor = (int) ((0.4 * files.length) + 1);
  84.             Arrays.sort(files, new FileLastModifSort());
  85.             for (int i = 0; i < removeFactor; i++) {
  86.                 if (files[i].getName().contains(WHOLESALE_CONV)) {
  87.                     files[i].delete();
  88.                 }
  89.             }
  90.         }
  91.                                                             
  92.         if (freeSpaceOnSd() <= CACHE_SIZE) {
  93.             return false;
  94.         }
  95.                                                                     
  96.         return true;
  97.     }
  98.                                                                 
  99.     /** 修改文件的最后修改时间 **/
  100.     public void updateFileTime(String path) {
  101.         File file = new File(path);
  102.         long newModifiedTime = System.currentTimeMillis();
  103.         file.setLastModified(newModifiedTime);
  104.     }
  105.                                                                 
  106.     /** 计算sdcard上的剩余空间 **/
  107.     private int freeSpaceOnSd() {
  108.         StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
  109.         double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
  110.         return (int) sdFreeMB;
  111.     }
  112.                                                                 
  113.     /** 将url转成文件名 **/
  114.     private String convertUrlToFileName(String url) {
  115.         String[] strs = url.split("/");
  116.         return strs[strs.length - 1] + WHOLESALE_CONV;
  117.     }
  118.                                                                 
  119.     /** 获得缓存目录 **/
  120.     private String getDirectory() {
  121.         String dir = getSDPath() + "/" + CACHDIR;
  122.         return dir;
  123.     }
  124.                                                                 
  125.     /** 取SD卡路径 **/
  126.     private String getSDPath() {
  127.         File sdDir = null;
  128.         boolean sdCardExist = Environment.getExternalStorageState().equals(
  129.                 android.os.Environment.MEDIA_MOUNTED); //判断sd卡是否存在
  130.         if (sdCardExist) {
  131.             sdDir = Environment.getExternalStorageDirectory(); //获取根目录
  132.         }
  133.         if (sdDir != null) {
  134.             return sdDir.toString();
  135.         } else {
  136.             return "";
  137.         }
  138.     }
  139.                                                             
  140.     /**
  141.      * 根据文件的最后修改时间进行排序
  142.      */
  143.     private class FileLastModifSort implements Comparator<File> {
  144.         public int compare(File arg0, File arg1) {
  145.             if (arg0.lastModified() > arg1.lastModified()) {
  146.                 return 1;
  147.             } else if (arg0.lastModified() == arg1.lastModified()) {
  148.                 return 0;
  149.             } else {
  150.                 return -1;
  151.             }
  152.         }
  153.     }
  154.                                                             
  155. }
从网络获取图片:



点击(此处)折叠或打开

  1. public class ImageGetFromHttp {
  2.     private static final String LOG_TAG = "ImageGetFromHttp";
  3.                                                            
  4.     public static Bitmap downloadBitmap(String url) {
  5.         final HttpClient client = new DefaultHttpClient();
  6.         final HttpGet getRequest = new HttpGet(url);
  7.                                                                
  8.         try {
  9.             HttpResponse response = client.execute(getRequest);
  10.             final int statusCode = response.getStatusLine().getStatusCode();
  11.             if (statusCode != HttpStatus.SC_OK) {
  12.                 Log.w(LOG_TAG, "Error " + statusCode + " while retrieving bitmap from " + url);
  13.                 return null;
  14.             }
  15.                                                                    
  16.             final HttpEntity entity = response.getEntity();
  17.             if (entity != null) {
  18.                 InputStream inputStream = null;
  19.                 try {
  20.                     inputStream = entity.getContent();
  21.                     FilterInputStream fit = new FlushedInputStream(inputStream);
  22.                     return BitmapFactory.decodeStream(fit);
  23.                 } finally {
  24.                     if (inputStream != null) {
  25.                         inputStream.close();
  26.                         inputStream = null;
  27.                     }
  28.                     entity.consumeContent();
  29.                 }
  30.             }
  31.         } catch (IOException e) {
  32.             getRequest.abort();
  33.             Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
  34.         } catch (IllegalStateException e) {
  35.             getRequest.abort();
  36.             Log.w(LOG_TAG, "Incorrect URL: " + url);
  37.         } catch (Exception e) {
  38.             getRequest.abort();
  39.             Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
  40.         } finally {
  41.             client.getConnectionManager().shutdown();
  42.         }
  43.         return null;
  44.     }
  45.                                                        
  46.     /*
  47.      * An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
  48.      */
  49.     static class FlushedInputStream extends FilterInputStream {
  50.         public FlushedInputStream(InputStream inputStream) {
  51.             super(inputStream);
  52.         }
  53.                                                        
  54.         @Override
  55.         public long skip(long n) throws IOException {
  56.             long totalBytesSkipped = 0L;
  57.             while (totalBytesSkipped < n) {
  58.                 long bytesSkipped = in.skip(n - totalBytesSkipped);
  59.                 if (bytesSkipped == 0L) {
  60.                     int b = read();
  61.                     if (b < 0) {
  62.                         break; // we reached EOF
  63.                     } else {
  64.                         bytesSkipped = 1; // we read one byte
  65.                     }
  66.                 }
  67.                 totalBytesSkipped += bytesSkipped;
  68.             }
  69.             return totalBytesSkipped;
  70.         }
  71.     }
  72. }
最后,获取一张图片的流程就如下代码所示:

点击(此处)折叠或打开

  1. /*** 获得一张图片,从三个地方获取,首先是内存缓存,然后是文件缓存,最后从网络获取 ***/
  2. public Bitmap getBitmap(String url) {
  3.     // 从内存缓存中获取图片
  4.     Bitmap result = memoryCache.getBitmapFromCache(url);
  5.     if (result == null) {
  6.         // 文件缓存中获取
  7.         result = fileCache.getImage(url);
  8.         if (result == null) {
  9.             // 从网络获取
  10.             result = ImageGetFromHttp.downloadBitmap(url);
  11.             if (result != null) {
  12.                 fileCache.saveBitmap(result, url);
  13.                 memoryCache.addBitmapToCache(url, result);
  14.             }
  15.         } else {
  16.             // 添加到内存缓存
  17.             memoryCache.addBitmapToCache(url, result);
  18.         }
  19.     }
  20.     return result;
  21. }



阅读(1324) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~