android知识回顾----bitmap和cache

来源:互联网 发布:安卓程序员待遇 编辑:程序博客网 时间:2024/06/05 06:49

1.bitmap的高效加载

BitmapFactory 类为我们提供了几种decoding方法(decodeByteArray(), decodeFile(),decodeResource(), etc)来从不同的来源创建出 Bitmap ,如何选择最恰当的decode方法取决于你的图片数据来源,这些方法都会去尝试申请内存来构建Bitmap对象,所有很容易就会导致一个OutOfMemory  异常,每种类型的decode方法都有额外的签名来让你通过 BitmapFactory.Options 类来指定decoding选项,当我们decoding的时候把inJustDecodeBounds 属性设置为true 可以避免申请内存,虽然会返回一个null Bitmap对象 ,但是会为我们传入的BitmapFactory.Options  对象设置 outWidth, outHeight and outMimeType 等属性的值,这个技术可以让你在构建Bitmap对象之前事先知道它的大小和类型


[java]
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 
BitmapFactory.decodeResource(getResources(), R.id.myimage, options); 
int imageHeight = options.outHeight; 
int imageWidth = options.outWidth; 
String imageType = options.outMimeType; 

为了避免java.lang.OutOfMemory  异常,在decoding Bitmap之前你有必要去检测Bitmap的大小和类型,除非你真的是非常清楚你要decoding的Bitmap的大小,还有这个大小要适合当前应用内存环境


载入‘缩小版’的Bitmap到内存


     现在我们已经知道了Bitmap的大小,这将有助于我们来决策是载入整张Bitmap还是载入'缩小版'的Bitmap,这里有一些因素需要进行考虑


            一、载入整张图片预计要使用多少内存
            二、在考虑到其它方面内存需要的情况下,你想把多少数量的内存给Bitmap使用
            三、用于显示Bitmap的 ImageView 控件或其它UI元件的大小
            四、当前设备屏幕的大小和密度
    
例如,一点都不值得载入1024x768  像素的图片到内存中,而最终只在128x96 像素大小的 ImageView 控件上显示 

public static int calculateInSampleSize( 
            BitmapFactory.Options options, int reqWidth, int reqHeight) { 
    // Raw height and width of image  
    final int height = options.outHeight; 
    final int width = options.outWidth; 
    int inSampleSize = 1; 
 
    if (height > reqHeight || width > reqWidth) { 
        if (width > height) { 
            inSampleSize = Math.round((float)height / (float)reqHeight); 
        } else { 
            inSampleSize = Math.round((float)width / (float)reqWidth); 
        } 
    } 
    return inSampleSize; 

NOTE :  inSampleSize  值是2的幂的话,对于decoder来说会更快和更高效。然而,如果你想把调整过大小的位图缓存到内存或硬盘上时,依然非常有意义decoding最合适的位图大小,这样有助于节省内存或节省硬盘空间


下面是一个获取位图的方法
[java]
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, 
        int reqWidth, int reqHeight) { 
 
    // First decode with inJustDecodeBounds=true to check dimensions  
    final BitmapFactory.Options options = new BitmapFactory.Options(); 
    options.inJustDecodeBounds = true; 
    BitmapFactory.decodeResource(res, resId, options); 
 
    // Calculate inSampleSize  
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 
 
    // Decode bitmap with inSampleSize set  
    options.inJustDecodeBounds = false; 
    return BitmapFactory.decodeResource(res, resId, options); 

 2.android中的缓存策略

 lRU  近期使用最小算法,当缓存满时,优先淘汰最近使用较少的缓存。

包括 LRUcache 和DiskLRUcache  


内存缓存策略

当有一个图片要去从网络下载的时候,我们并不会直接去从网络下载,因为在这个时代,用户的流量是宝贵的,耗流量的应用是不会得到用户的青睐的。那我们该怎么办呢?这样,我们会先从内存缓存中去查找是否有该图片,如果没有就去文件缓存中查找是否有该图片,如果还没有,我们就从网络下载图片。本博文的侧重点是如何做内存缓存,内存缓存的查找策略是:先从强引用缓存中查找,如果没有再从软引用缓存中查找,如果在软引用缓存中找到了,就把它移入强引用缓存;如果强引用缓存满了,就会根据Lru算法把某些图片移入软引用缓存,如果软引用缓存也满了,最早的软引用就会被删除。这里,我有必要说明下几个概念:强引用、软引用、弱引用、Lru。

强引用:就是直接引用一个对象,一般的对象引用均是强引用

软引用:引用一个对象,当内存不足并且除了我们的引用之外没有其他地方引用此对象的情况 下,该对象会被gc回收

弱引用:引用一个对象,当除了我们的引用之外没有其他地方引用此对象的情况下,只要gc被调用,它就会被回收(请注意它和软引用的区别)

Lru:Least Recently Used 近期最少使用算法,是一种页面置换算法,其思想是在缓存的页面数目固定的情况下,那些最近使用次数最少的页面将被移出,对于我们的内存缓存来说,强引用缓存大小固定为4M,如果当缓存的图片大于4M的时候,有些图片就会被从强引用缓存中删除,哪些图片会被删除呢,就是那些近期使用次数最少的图片。

代码

[java] view plain copy
  1. public class ImageMemoryCache {  
  2.     /** 
  3.      * 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。 
  4.      *  强引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。 
  5.      */  
  6.     private static final String TAG = "ImageMemoryCache";  
  7.   
  8.     private static LruCache<String, Bitmap> mLruCache; // 强引用缓存  
  9.   
  10.     private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; // 软引用缓存  
  11.   
  12.     private static final int LRU_CACHE_SIZE = 4 * 1024 * 1024// 强引用缓存容量:4MB  
  13.   
  14.     private static final int SOFT_CACHE_NUM = 20// 软引用缓存个数  
  15.   
  16.     // 在这里分别初始化强引用缓存和弱引用缓存  
  17.     public ImageMemoryCache() {  
  18.         mLruCache = new LruCache<String, Bitmap>(LRU_CACHE_SIZE) {  
  19.             @Override  
  20.             // sizeOf返回为单个hashmap value的大小  
  21.             protected int sizeOf(String key, Bitmap value) {  
  22.                 if (value != null)  
  23.                     return value.getRowBytes() * value.getHeight();  
  24.                 else  
  25.                     return 0;  
  26.             }  
  27.   
  28.             @Override  
  29.             protected void entryRemoved(boolean evicted, String key,  
  30.                     Bitmap oldValue, Bitmap newValue) {  
  31.                 if (oldValue != null) {  
  32.                     // 强引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存  
  33.                     Logger.d(TAG, "LruCache is full,move to SoftRefernceCache");  
  34.                     mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));  
  35.                 }  
  36.             }  
  37.         };  
  38.   
  39.         mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(  
  40.                 SOFT_CACHE_NUM, 0.75f, true) {  
  41.             private static final long serialVersionUID = 1L;  
  42.   
  43.             /** 
  44.              * 当软引用数量大于20的时候,最旧的软引用将会被从链式哈希表中移出 
  45.              */  
  46.             @Override  
  47.             protected boolean removeEldestEntry(  
  48.                     Entry<String, SoftReference<Bitmap>> eldest) {  
  49.                 if (size() > SOFT_CACHE_NUM) {  
  50.                     Logger.d(TAG, "should remove the eldest from SoftReference");  
  51.                     return true;  
  52.                 }  
  53.                 return false;  
  54.             }  
  55.         };  
  56.     }  
  57.   
  58.     /** 
  59.      * 从缓存中获取图片 
  60.      */  
  61.     public Bitmap getBitmapFromMemory(String url) {  
  62.         Bitmap bitmap;  
  63.   
  64.         // 先从强引用缓存中获取  
  65.         synchronized (mLruCache) {  
  66.             bitmap = mLruCache.get(url);  
  67.             if (bitmap != null) {  
  68.                 // 如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除  
  69.                 mLruCache.remove(url);  
  70.                 mLruCache.put(url, bitmap);  
  71.                 Logger.d(TAG, "get bmp from LruCache,url=" + url);  
  72.                 return bitmap;  
  73.             }  
  74.         }  
  75.   
  76.         // 如果强引用缓存中找不到,到软引用缓存中找,找到后就把它从软引用中移到强引用缓存中  
  77.         synchronized (mSoftCache) {  
  78.             SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);  
  79.             if (bitmapReference != null) {  
  80.                 bitmap = bitmapReference.get();  
  81.                 if (bitmap != null) {  
  82.                     // 将图片移回LruCache  
  83.                     mLruCache.put(url, bitmap);  
  84.                     mSoftCache.remove(url);  
  85.                     Logger.d(TAG, "get bmp from SoftReferenceCache, url=" + url);  
  86.                     return bitmap;  
  87.                 } else {  
  88.                     mSoftCache.remove(url);  
  89.                 }  
  90.             }  
  91.         }  
  92.         return null;  
  93.     }  
  94.   
  95.     /** 
  96.      * 添加图片到缓存 
  97.      */  
  98.     public void addBitmapToMemory(String url, Bitmap bitmap) {  
  99.         if (bitmap != null) {  
  100.             synchronized (mLruCache) {  
  101.                 mLruCache.put(url, bitmap);  
  102.             }  
  103.         }  
  104.     }  
  105.   
  106.     public void clearCache() {  
  107.         mSoftCache.clear();  
  108.     }  
  109. }  



文件缓存策略

当一张图片从网络下载成功以后,这个图片会被加入内存缓存和文件缓存,对于文件缓存来说,这张图片将被以url的哈希值加cach后缀名的形式存储在SD卡上,这样,当下一次再需要同一个url的图片的时候,就不需要从网络下载了,而是直接通过url来进行查找。同时一张图片被访问时,它的最后修改时间将被更新,这样的意义在于:当SD卡空间不足的时候,将会按照最后修改时间来删除40%缓存的图片,确切来说,那些修改时间比较早的图片将会被删除。

代码展示

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class ImageFileCache  
  2. {  
  3.     private static final String TAG = "ImageFileCache";  
  4.       
  5.     //图片缓存目录  
  6.     private static final String IMGCACHDIR = "/sdcard/ImgCach";  
  7.       
  8.     //保存的cache文件宽展名  
  9.     private static final String CACHETAIL = ".cach";  
  10.                                                               
  11.     private static final int MB = 1024*1024;  
  12.       
  13.     private static final int CACHE_SIZE = 1;  
  14.       
  15.     //当SD卡剩余空间小于10M的时候会清理缓存  
  16.     private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;  
  17.                                                                   
  18.     public ImageFileCache()   
  19.     {  
  20.         //清理部分文件缓存  
  21.         removeCache(IMGCACHDIR);              
  22.     }  
  23.                                                                   
  24.     /**  
  25.      * 从缓存中获取图片  
  26.      */  
  27.     public Bitmap getImageFromFile(final String url)   
  28.     {      
  29.         final String path = IMGCACHDIR + "/" + convertUrlToFileName(url);  
  30.         File file = new File(path);  
  31.         if (file != null && file.exists())   
  32.         {  
  33.             Bitmap bmp = BitmapFactory.decodeFile(path);  
  34.             if (bmp == null)   
  35.             {  
  36.                 file.delete();  
  37.             }   
  38.             else   
  39.             {  
  40.                 updateFileTime(path);  
  41.                 Logger.d(TAG, "get bmp from FileCache,url=" + url);  
  42.                 return bmp;  
  43.             }  
  44.         }  
  45.         return null;  
  46.     }  
  47.                                                                   
  48.     /** 
  49.      * 将图片存入文件缓存  
  50.      */  
  51.     public void saveBitmapToFile(Bitmap bm, String url)   
  52.     {  
  53.         if (bm == null) {  
  54.             return;  
  55.         }  
  56.         //判断sdcard上的空间  
  57.         if (FREE_SD_SPACE_NEEDED_TO_CACHE > SdCardFreeSpace())   
  58.         {  
  59.             //SD空间不足  
  60.             return;  
  61.         }  
  62.           
  63.         String filename = convertUrlToFileName(url);  
  64.         File dirFile = new File(IMGCACHDIR);  
  65.         if (!dirFile.exists())  
  66.             dirFile.mkdirs();  
  67.         File file = new File(IMGCACHDIR +"/" + filename);  
  68.         try   
  69.         {  
  70.             file.createNewFile();  
  71.             OutputStream outStream = new FileOutputStream(file);  
  72.             bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);  
  73.             outStream.flush();  
  74.             outStream.close();  
  75.         }   
  76.         catch (FileNotFoundException e)   
  77.         {  
  78.             Logger.d(TAG, "FileNotFoundException");  
  79.         }   
  80.         catch (IOException e)   
  81.         {  
  82.             Logger.d(TAG, "IOException");  
  83.         }  
  84.     }   
  85.                                                                   
  86.     /** 
  87.      * 计算存储目录下的文件大小, 
  88.      * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定 
  89.      * 那么删除40%最近没有被使用的文件 
  90.      */  
  91.     private boolean removeCache(String dirPath)   
  92.     {  
  93.         File dir = new File(dirPath);  
  94.         File[] files = dir.listFiles();  
  95.           
  96.         if (files == null)   
  97.         {  
  98.             return true;  
  99.         }  
  100.           
  101.         if (!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))  
  102.         {  
  103.             return false;  
  104.         }  
  105.                                                               
  106.         int dirSize = 0;  
  107.         for (int i = 0; i < files.length; i++)   
  108.         {  
  109.             if (files[i].getName().contains(CACHETAIL))   
  110.             {  
  111.                 dirSize += files[i].length();  
  112.             }  
  113.         }  
  114.                                                               
  115.         if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > SdCardFreeSpace())   
  116.         {  
  117.             int removeFactor = (int) (0.4 * files.length);  
  118.             Arrays.sort(files, new FileLastModifSort());  
  119.             for (int i = 0; i < removeFactor; i++)   
  120.             {  
  121.                 if (files[i].getName().contains(CACHETAIL))   
  122.                 {  
  123.                     files[i].delete();  
  124.                 }  
  125.             }  
  126.         }  
  127.                                                               
  128.         if (SdCardFreeSpace() <= CACHE_SIZE)   
  129.         {  
  130.             return false;  
  131.         }  
  132.                                                                       
  133.         return true;  
  134.     }  
  135.                                                                   
  136.     /** 
  137.      * 修改文件的最后修改时间 
  138.      */  
  139.     public void updateFileTime(String path)   
  140.     {  
  141.         File file = new File(path);  
  142.         long newModifiedTime = System.currentTimeMillis();  
  143.         file.setLastModified(newModifiedTime);  
  144.     }  
  145.                                                                   
  146.     /**  
  147.      * 计算SD卡上的剩余空间  
  148.      */  
  149.     private int SdCardFreeSpace()  
  150.     {  
  151.         StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());  
  152.         double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;  
  153.         return (int) sdFreeMB;  
  154.     }   
  155.                                                                   
  156.     /**  
  157.      * 将url转成文件名  
  158.      */  
  159.     private String convertUrlToFileName(String url)  
  160.     {  
  161.         return url.hashCode() + CACHETAIL;  
  162.     }  
  163.                                                                   
  164.     /** 
  165.      * 根据文件的最后修改时间进行排序 
  166.      */  
  167.     private class FileLastModifSort implements Comparator<File>   
  168.     {  
  169.         public int compare(File file0, File file1)   
  170.         {  
  171.             if (file0.lastModified() > file1.lastModified())  
  172.             {  
  173.                 return 1;  
  174.             }   
  175.             else if (file0.lastModified() == file1.lastModified())   
  176.             {  
  177.                 return 0;  
  178.             }   
  179.             else   
  180.             {  
  181.                 return -1;  
  182.             }  
  183.         }  
  184.     }  
  185.   
  186. }  


0 0
原创粉丝点击