android 加载图片轻松避免OOM(out of memory)

简介:

在使用android加载图片的时候,经常会出现内存溢出,主要是由于android可使用的内存太小,而通过代码加载进来的图片,并不会被GC回收,于是我写了一个工具类用来加载图片,并且建立缓存,轻松避免内存溢出,废话不多说,上代码

[java]  view plain copy
  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. import java.util.HashMap;  
  9. import java.util.HashSet;  
  10. import java.util.LinkedList;  
  11. import java.util.Map;  
  12. import java.util.Queue;  
  13. import java.util.Set;  
  14.   
  15. import android.graphics.Bitmap;  
  16. import android.graphics.BitmapFactory;  
  17. import android.graphics.BitmapFactory.Options;  
  18.   
  19. /** 
  20.  * Bitmap工具类,缓存用过的指定数量的图片,使用此工具类,不再需要手动管理Bitmap内存 原理: 
  21.  * 用一个队列保存使用Bitmap的顺序,每次使用Bitmap将对象移动到队列头 当内存不够,或者达到制定的缓存数量的时候,回收队列尾部图片 
  22.  * 保证当前使用最多的图片得到最长时间的缓存,提高速度 
  23.  *  
  24.  * @author liaoxingliao 
  25.  *  
  26.  */  
  27. public final class BitMapUtil {  
  28.   
  29.     private static final Size ZERO_SIZE = new Size(00);  
  30.     private static final Options OPTIONS_GET_SIZE = new Options();  
  31.     private static final Options OPTIONS_DECODE = new Options();  
  32.     private static final byte[] LOCKED = new byte[0];  
  33.   
  34.     private static final LinkedList<String> CACHE_ENTRIES = new LinkedList<String>(); // 此对象用来保持Bitmap的回收顺序,保证最后使用的图片被回收  
  35.     private static final Queue<QueueEntry> TASK_QUEUE = new LinkedList<QueueEntry>(); // 线程请求创建图片的队列  
  36.     private static final Set<String> TASK_QUEUE_INDEX = new HashSet<String>(); // 保存队列中正在处理的图片的key,有效防止重复添加到请求创建队列  
  37.   
  38.     private static final Map<String, Bitmap> IMG_CACHE_INDEX = new HashMap<String, Bitmap>(); // 缓存Bitmap  
  39.                                                                                                 // 通过图片路径,图片大小  
  40.   
  41.     private static int CACHE_SIZE = 200// 缓存图片数量  
  42.   
  43.     static {  
  44.         OPTIONS_GET_SIZE.inJustDecodeBounds = true;  
  45.         // 初始化创建图片线程,并等待处理  
  46.         new Thread() {  
  47.             {  
  48.                 setDaemon(true);  
  49.             }  
  50.   
  51.             public void run() {  
  52.                 while (true) {  
  53.                     synchronized (TASK_QUEUE) {  
  54.                         if (TASK_QUEUE.isEmpty()) {  
  55.                             try {  
  56.                                 TASK_QUEUE.wait();  
  57.                             } catch (InterruptedException e) {  
  58.                                 e.printStackTrace();  
  59.                             }  
  60.                         }  
  61.                     }  
  62.                     QueueEntry entry = TASK_QUEUE.poll();  
  63.                     String key = createKey(entry.path, entry.width,  
  64.                             entry.height);  
  65.                     TASK_QUEUE_INDEX.remove(key);  
  66.                     //createBitmap(entry.path, entry.width, entry.height);  
  67.                     //修正过的代码  
  68.                     getBitmap(entry.path,entry.width,entry.height);  
  69.                 }  
  70.             }  
  71.         }.start();  
  72.   
  73.     }  
  74.   
  75.     /** 
  76.      * 创建一张图片 如果缓存中已经存在,则返回缓存中的图,否则创建一个新的对象,并加入缓存 
  77.      * 宽度,高度,为了缩放原图减少内存的,如果输入的宽,高,比原图大,返回原图 
  78.      *  
  79.      * @param path      图片物理路径 (必须是本地路径,不能是网络路径) 
  80.      * @param width     需要的宽度 
  81.      * @param height    需要的高度 
  82.      * @return 
  83.      */  
  84.     public static Bitmap getBitmap(String path, int width, int height) {  
  85.         Bitmap bitMap = null;  
  86.         try {  
  87.             if (CACHE_ENTRIES.size() >= CACHE_SIZE) {  
  88.                 destoryLast();  
  89.             }  
  90.             bitMap = useBitmap(path, width, height);  
  91.             if (bitMap != null && !bitMap.isRecycled()) {  
  92.                 return bitMap;  
  93.             }  
  94.             bitMap = createBitmap(path, width, height);  
  95.             String key = createKey(path, width, height);  
  96.             synchronized (LOCKED) {  
  97.                 IMG_CACHE_INDEX.put(key, bitMap);  
  98.                 CACHE_ENTRIES.addFirst(key);  
  99.             }  
  100.         } catch (OutOfMemoryError err) {  
  101.             destoryLast();  
  102.             System.out.println(CACHE_SIZE);  
  103.             //return createBitmap(path, width, height);  
  104.             //修正过的代码  
  105.             return getBitmap(path, width, height);  
  106.         }  
  107.         return bitMap;  
  108.     }  
  109.   
  110.     /** 
  111.      * 设置缓存图片数量 如果输入负数,会产生异常 
  112.      *  
  113.      * @param size 
  114.      */  
  115.     public static void setCacheSize(int size) {  
  116.         if (size <= 0) {  
  117.             throw new RuntimeException("size :" + size);  
  118.         }  
  119.         while (size < CACHE_ENTRIES.size()) {  
  120.             destoryLast();  
  121.         }  
  122.         CACHE_SIZE = size;  
  123.     }  
  124.   
  125.     /** 
  126.      * 加入一个图片处理请求到图片创建队列 
  127.      *  
  128.      * @param path 
  129.      *            图片路径(本地) 
  130.      * @param width 
  131.      *            图片宽度 
  132.      * @param height 
  133.      *            图片高度 
  134.      */  
  135.     public static void addTask(String path, int width, int height) {  
  136.         QueueEntry entry = new QueueEntry();  
  137.         entry.path = path;  
  138.         entry.width = width;  
  139.         entry.height = height;  
  140.         synchronized (TASK_QUEUE) {  
  141.             String key = createKey(path, width, height);  
  142.             if (!TASK_QUEUE_INDEX.contains(key)  
  143.                     && !IMG_CACHE_INDEX.containsKey(key)) {  
  144.                 TASK_QUEUE.add(entry);  
  145.                 TASK_QUEUE_INDEX.add(key);  
  146.                 TASK_QUEUE.notify();  
  147.             }  
  148.         }  
  149.     }  
  150.       
  151.     /** 
  152.      * 通过图片路径返回图片实际大小 
  153.      * @param path      图片物理路径 
  154.      * @return 
  155.      */  
  156.     public static Size getBitMapSize(String path) {  
  157.         File file = new File(path);  
  158.         if (file.exists()) {  
  159.             InputStream in = null;  
  160.             try {  
  161.                 in = new FileInputStream(file);  
  162.                 BitmapFactory.decodeStream(in, null, OPTIONS_GET_SIZE);  
  163.                 return new Size(OPTIONS_GET_SIZE.outWidth,  
  164.                         OPTIONS_GET_SIZE.outHeight);  
  165.             } catch (FileNotFoundException e) {  
  166.                 return ZERO_SIZE;  
  167.             } finally {  
  168.                 closeInputStream(in);  
  169.             }  
  170.         }  
  171.         return ZERO_SIZE;  
  172.     }  
  173.   
  174.     // ------------------------------------------------------------------ private Methods  
  175.     // 将图片加入队列头  
  176.     private static Bitmap useBitmap(String path, int width, int height) {  
  177.         Bitmap bitMap = null;  
  178.         String key = createKey(path, width, height);  
  179.         synchronized (LOCKED) {  
  180.             bitMap = IMG_CACHE_INDEX.get(key);  
  181.             if (null != bitMap) {  
  182.                 if (CACHE_ENTRIES.remove(key)) {  
  183.                     CACHE_ENTRIES.addFirst(key);  
  184.                 }  
  185.             }  
  186.         }  
  187.         return bitMap;  
  188.     }  
  189.   
  190.     // 回收最后一张图片  
  191.     private static void destoryLast() {  
  192.         synchronized (LOCKED) {  
  193.             String key = CACHE_ENTRIES.removeLast();  
  194.             if (key.length() > 0) {  
  195.                 Bitmap bitMap = IMG_CACHE_INDEX.remove(key);  
  196.                 if (bitMap != null && !bitMap.isRecycled()) {  
  197.                     bitMap.recycle();  
  198.                     bitMap = null;  
  199.                 }  
  200.             }  
  201.         }  
  202.     }  
  203.   
  204.     // 创建键  
  205.     private static String createKey(String path, int width, int height) {  
  206.         if (null == path || path.length() == 0) {  
  207.             return "";  
  208.         }  
  209.         return path + "_" + width + "_" + height;  
  210.     }  
  211.   
  212.     // 通过图片路径,宽度高度创建一个Bitmap对象  
  213.     private static Bitmap createBitmap(String path, int width, int height) {  
  214.         File file = new File(path);  
  215.         if (file.exists()) {  
  216.             InputStream in = null;  
  217.             try {  
  218.                 in = new FileInputStream(file);  
  219.                 Size size = getBitMapSize(path);  
  220.                 if (size.equals(ZERO_SIZE)) {  
  221.                     return null;  
  222.                 }  
  223.                 int scale = 1;  
  224.                 int a = size.getWidth() / width;  
  225.                 int b = size.getHeight() / height;  
  226.                 scale = Math.max(a, b);  
  227.                 synchronized (OPTIONS_DECODE) {  
  228.                     OPTIONS_DECODE.inSampleSize = scale;  
  229.                     Bitmap bitMap = BitmapFactory.decodeStream(in, null,  
  230.                             OPTIONS_DECODE);  
  231.                     return bitMap;  
  232.                 }  
  233.             } catch (FileNotFoundException e) {  
  234.                 e.printStackTrace();  
  235.             } finally {  
  236.                 closeInputStream(in);  
  237.             }  
  238.         }  
  239.         return null;  
  240.     }  
  241.       
  242.     // 关闭输入流  
  243.     private static void closeInputStream(InputStream in) {  
  244.         if (null != in) {  
  245.             try {  
  246.                 in.close();  
  247.             } catch (IOException e) {  
  248.                 e.printStackTrace();  
  249.             }  
  250.         }  
  251.     }  
  252.   
  253.     // 图片大小  
  254.     static class Size {  
  255.         private int width, height;  
  256.   
  257.         Size(int width, int height) {  
  258.             this.width = width;  
  259.             this.height = height;  
  260.         }  
  261.   
  262.         public int getWidth() {  
  263.             return width;  
  264.         }  
  265.   
  266.         public int getHeight() {  
  267.             return height;  
  268.         }  
  269.     }  
  270.   
  271.     // 队列缓存参数对象  
  272.     static class QueueEntry {  
  273.         public String path;  
  274.         public int width;  
  275.         public int height;  
  276.     }  
  277. }  


相关文章
|
Java Android开发
Android oom pthread_create (1040KB stack)分析及解决
Android oom pthread_create (1040KB stack)分析及解决
200 0
|
Android开发
【错误记录】Android Studio 编译报错 ( Error:Connection timed out: connect | 更新配置依赖仓库方式 )
【错误记录】Android Studio 编译报错 ( Error:Connection timed out: connect | 更新配置依赖仓库方式 )
943 0
【错误记录】Android Studio 编译报错 ( Error:Connection timed out: connect | 更新配置依赖仓库方式 )
|
Java Android开发 开发者
Android Studio Profiler Memory (内存分析工具)的简单使用及问题分析
Android Studio Profiler Memory (内存分析工具)的简单使用及问题分析
2864 0
Android Studio Profiler Memory (内存分析工具)的简单使用及问题分析
|
Java 开发工具 Maven
Android 编译 gradle 内存 OOM 解决之路(二)
Android 编译 gradle 内存 OOM 解决之路
|
Java Android开发
Android 编译 gradle 内存 OOM 解决之路(一)
Android 编译 gradle 内存 OOM 解决之路
|
XML 缓存 数据库
Android Glide加载图片、网络监听、设置资源监听
Android Glide加载图片、网络监听、设置资源监听
719 0
Android Glide加载图片、网络监听、设置资源监听
|
Android开发
Android RV通过Glide加载图片闪一下
Android RV通过Glide加载图片闪一下
142 0
Android RV通过Glide加载图片闪一下
|
前端开发 Java Linux
内存泄露,OOM,ANR ,Devik 进程,Framework原理,Activity 生成一个 view,Android 中的动画,SurfaceView和V
内存泄露,OOM,ANR ,Devik 进程,Framework原理,Activity 生成一个 view,Android 中的动画,SurfaceView和V
238 0
|
Java Linux Android开发
转 - Android下一次OOM调试过程
线程数超限,即proc/pid/status中记录的线程数(threads项)突破/proc/sys/kernel/threads-max中规定的最大线程数。
122 0
|
算法 Java Android开发
Android性能优化 | 帧动画OOM?优化帧动画之SurfaceView逐帧解析
应用 SurfaceView 逐帧绘制帧动画配合 Bitmap 复用。和原生帧动画的内存压力及卡顿说再见!
925 0