android 加载图片轻松避免OOM(out of memory) 支持设置缓存大小,不再强制catch OOM

来源:互联网 发布:动易cms 报价 编辑:程序博客网 时间:2024/06/08 15:13
[java] view plaincopyprint?
  1. package l.test1.util;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileNotFoundException;  
  6. import java.io.IOException;  
  7. import java.io.InputStream;  
  8.   
  9. import android.graphics.Bitmap;  
  10. import android.graphics.BitmapFactory;  
  11. import android.graphics.BitmapFactory.Options;  
  12.   
  13. public class BitmapUtil {  
  14.     private static final Size ZERO_SIZE = new Size(0,0);  
  15.     private static final Options OPTIONS_GET_SIZE = new Options();  
  16.     private static final Options OPTIONS_DECODE = new Options();  
  17.       
  18.     static{  
  19.         OPTIONS_GET_SIZE.inJustDecodeBounds = true;  
  20.     }  
  21.       
  22.     public static Size getBitMapSize(String path){  
  23.         File file = new File(path);  
  24.         if(file.exists()){  
  25.             InputStream in = null;  
  26.             try{  
  27.                 in = new FileInputStream(file);  
  28.                 BitmapFactory.decodeStream(in, null, OPTIONS_GET_SIZE);  
  29.                 return new Size(OPTIONS_GET_SIZE.outWidth,OPTIONS_GET_SIZE.outHeight);  
  30.             } catch (FileNotFoundException e) {  
  31.                 return ZERO_SIZE;  
  32.             }finally{  
  33.                 closeInputStream(in);  
  34.             }  
  35.         }  
  36.         return ZERO_SIZE;  
  37.     }  
  38.       
  39.     public static Bitmap createBitmap(String path,int width,int height){  
  40.         File file = new File(path);  
  41.         if(file.exists()){  
  42.             InputStream in = null;  
  43.             try {  
  44.                 in = new FileInputStream(file);  
  45.                 Size size = getBitMapSize(path);  
  46.                 if(size.equals(ZERO_SIZE)){  
  47.                     return null;  
  48.                 }  
  49.                 int scale = 1;  
  50.                 int a = size.getWidth() / width;  
  51.                 int b = size.getHeight() / height;  
  52.                 scale = Math.max(a, b);  
  53.                 synchronized (OPTIONS_DECODE) {  
  54.                     OPTIONS_DECODE.inSampleSize = scale;  
  55.                     Bitmap bitMap =  BitmapFactory.decodeStream(in,null,OPTIONS_DECODE);  
  56.                     return bitMap;  
  57.                 }  
  58.             } catch (FileNotFoundException e) {  
  59.                 e.printStackTrace();  
  60.             }   
  61.             finally{  
  62.                 closeInputStream(in);  
  63.             }  
  64.         }  
  65.         return null;  
  66.     }  
  67.       
  68.     public static void destory(Bitmap bitmap){  
  69.         if(null != bitmap && !bitmap.isRecycled()){  
  70.             bitmap.recycle();  
  71.             bitmap = null;  
  72.         }  
  73.     }  
  74.       
  75.     private static void closeInputStream(InputStream in) {  
  76.         if(null != in){  
  77.             try {  
  78.                 in.close();  
  79.             } catch (IOException e) {  
  80.                 e.printStackTrace();  
  81.             }  
  82.         }  
  83.     }  
  84. }  
  85.   
  86. class Size{  
  87.     private int width,height;  
  88.     Size(int width,int height){  
  89.         this.width = width;  
  90.         this.height = height;  
  91.     }  
  92.     public int getWidth() {  
  93.         return width;  
  94.     }  
  95.     public int getHeight() {  
  96.         return height;  
  97.     }  
  98. }  
[java] view plaincopyprint?
  1. </pre><pre name="code" class="java">  
[java] view plaincopyprint?
  1. </pre><p></p><pre name="code" class="java">package l.test1.util;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.HashSet;  
  5. import java.util.LinkedList;  
  6. import java.util.Map;  
  7. import java.util.Set;  
  8. import java.util.Stack;  
  9.   
  10. import android.graphics.Bitmap;  
  11. /** 
  12.  * Bitmap工具类,缓存用过的指定数量的图片,使用此工具类,不再需要手动管理Bitmap内存 
  13.  * 原理: 
  14.  *      用一个队列保存使用Bitmap的顺序,每次使用Bitmap将对象移动到队列头 
  15.  *      当内存不够,或者达到制定的缓存数量的时候,回收队列尾部图片 
  16.  *      保证当前使用最多的图片得到最长时间的缓存,提高速度 
  17.  * @author liaoxingliao 
  18.  * 
  19.  */  
  20. public final class BitMapLRU {  
  21.       
  22.     private static int CACHE_BYTE_SIZE = 10*1024*1024//缓存10M图片  
  23.       
  24.     private static int CACHE_SIZE = 2000//缓存图片数量   
  25.       
  26.     private static int byteSize = 0;  
  27.       
  28.     private static final byte[] LOCKED = new byte[0];  
  29.       
  30.     private static final LinkedList<String> CACHE_ENTRIES =       //此对象用来保持Bitmap的回收顺序,保证最后使用的图片被回收  
  31.             new LinkedList<String>(){  
  32.         private static final long serialVersionUID = 1L;  
  33.         @Override  
  34.         public void addFirst(String object) {  
  35.             while(remove(object));  
  36.             super.addFirst(object);  
  37.         }  
  38.     };  
  39.     private static final Stack<QueueEntry> TASK_QUEUE = new Stack<QueueEntry>();        //线程请求创建图片的队列  
  40.     private static final Set<String> TASK_QUEUE_INDEX = new HashSet<String>();              //保存队列中正在处理的图片的key,有效防止重复添加到请求创建队列  
  41.     private static final Map<String,Bitmap> IMG_CACHE_INDEX = new HashMap<String,Bitmap>(); //缓存Bitmap 通过图片路径,图片大小  
  42.       
  43.     static{  
  44. //      初始化创建图片线程,并等待处理   
  45.         new Thread(){  
  46.             {setDaemon(true);}  
  47.             public void run() {  
  48.                 while(true) {  
  49.                     synchronized (TASK_QUEUE) {  
  50.                         if(TASK_QUEUE.isEmpty()) {  
  51.                             try {  
  52.                                 TASK_QUEUE.wait();  
  53.                             } catch (InterruptedException e) {  
  54.                                 e.printStackTrace();  
  55.                             }  
  56.                         }  
  57.                     }  
  58.                     QueueEntry entry = TASK_QUEUE.pop();  
  59.                     String key = createKey(entry.path,entry.width,entry.height);  
  60.                     TASK_QUEUE_INDEX.remove(key);  
  61.                     createBitmap(entry.path, entry.width, entry.height);  
  62.                 }  
  63.             }  
  64.         }.start();  
  65.     }  
  66.       
  67.     /** 
  68.      * 创建一张图片 如果缓存中已经存在,则返回缓存中的图,否则创建一个新的对象,并加入缓存 
  69.      * 宽度,高度,为了缩放原图减少内存的,如果输入的宽,高,比原图大,返回原图 
  70.      * @param path          图片物理路径 (必须是本地路径,不能是网络路径) 
  71.      * @param width         需要的宽度 
  72.      * @param height        需要的高度 
  73.      * @return 
  74.      */  
  75.     public static Bitmap createBitmap(String path, int width, int height) {  
  76.         Bitmap bitMap = null;  
  77.         try{  
  78.             while(CACHE_ENTRIES.size() >= CACHE_SIZE || byteSize >= CACHE_BYTE_SIZE) {  
  79.                 destoryLast();  
  80.             }  
  81.             bitMap = useBitmap(path, width, height);  
  82.             if( bitMap != null && !bitMap.isRecycled()){  
  83.                 return bitMap;//4294967296  
  84.   
  85.             }  
  86.             bitMap = BitmapUtil.createBitmap(path, width, height);  
  87.             if(bitMap == null) {  //可能不是有效的图片..  
  88.                 return null;   
  89.             }  
  90.             byteSize += (bitMap.getRowBytes() * bitMap.getHeight());  
  91.               
  92.             String key = createKey(path,width,height);  
  93.             synchronized (LOCKED) {  
  94.                 IMG_CACHE_INDEX.put(key, bitMap);  
  95.                 CACHE_ENTRIES.addFirst(key);  
  96.             }  
  97.         }catch(OutOfMemoryError err) {  
  98.             System.out.println("OOM:" + byteSize);  
  99.             destoryLast();  
  100.             return createBitmap(path, width, height);  
  101.         }  
  102.         return bitMap;  
  103.     }  
  104.       
  105.     /** 
  106.      * 设置缓存图片数量 如果输入负数,会产生异常 
  107.      * @param size 
  108.      */  
  109.     public static void setCacheSize(int size) {  
  110.         if(size <=0 ){  
  111.             throw new RuntimeException("size :"+size);  
  112.         }  
  113.         while(size < CACHE_ENTRIES.size()){  
  114.             destoryLast();  
  115.         }  
  116.         CACHE_SIZE = size;  
  117.     }  
  118.       
  119.     /** 
  120.      * 加入一个图片处理请求到图片创建队列 
  121.      * @param path          图片路径(本地) 
  122.      * @param width         图片宽度 
  123.      * @param height        图片高度 
  124.      */  
  125.     public static void addTask(String path, int width, int height) {  
  126.         QueueEntry entry = new QueueEntry();  
  127.         entry.path = path;  
  128.         entry.width = width;  
  129.         entry.height = height;  
  130.         synchronized (TASK_QUEUE) {  
  131.             while(TASK_QUEUE.size() > 20) {  
  132.                 QueueEntry e =  TASK_QUEUE.lastElement();  
  133.                 TASK_QUEUE.remove(e);  
  134.                 TASK_QUEUE_INDEX.remove(createKey(e.path, e.width, e.height));  
  135.             }  
  136.             String key = createKey(path,width,height);  
  137.             if(!TASK_QUEUE_INDEX.contains(key) && !IMG_CACHE_INDEX.containsKey(key)){  
  138.                 TASK_QUEUE.push(entry);  
  139.                 TASK_QUEUE_INDEX.add(key);  
  140.                 TASK_QUEUE.notify();  
  141.             }  
  142.         }  
  143.     }  
  144.       
  145.     public static void cleanTask() {  
  146.         synchronized (TASK_QUEUE) {  
  147.             TASK_QUEUE_INDEX.clear();  
  148.             TASK_QUEUE.clear();  
  149.         }  
  150.     }  
  151.       
  152.     // 将图片加入队列头   
  153.     private static Bitmap useBitmap(String path,int width,int height) {  
  154.         Bitmap bitMap = null;  
  155.         String key = createKey(path,width,height);  
  156.         synchronized (LOCKED) {  
  157.             bitMap = IMG_CACHE_INDEX.get(key);  
  158.             if(null != bitMap){  
  159.                 CACHE_ENTRIES.addFirst(key);  
  160.             }  
  161.         }  
  162.         return bitMap;  
  163.     }  
  164.       
  165.     // 回收最后一张图片   
  166.     private static void destoryLast() {  
  167.         synchronized (LOCKED) {  
  168.             if(!CACHE_ENTRIES.isEmpty()) {  
  169.                 String key = CACHE_ENTRIES.removeLast();  
  170.                 if(key.length() > 0) {  
  171.                     Bitmap bitMap = IMG_CACHE_INDEX.remove(key);  
  172.                     if(bitMap != null) {  
  173.                         bitMap.recycle();  
  174.                         byteSize -= (bitMap.getRowBytes() * bitMap.getHeight());  
  175.                         bitMap = null;  
  176.                     }  
  177.                 }  
  178.             }  
  179.         }  
  180.     }  
  181.       
  182.     // 创建键   
  183.     private static String createKey(String path,int width,int height) {  
  184.         if(null == path || path.length() == 0) {  
  185.             return "";  
  186.         }  
  187.         return path+"_"+width+"_"+height;  
  188.     }  
  189.   
  190.     // 队列缓存参数对象   
  191.     static class QueueEntry{  
  192.         public String path;  
  193.         public int width;  
  194.         public int height;  
  195.     }  
  196. }  
原创粉丝点击