android 加載圖片輕鬆避免OOM(out of memory) 支持設置緩存大小,不再強製catch OOM
https://blog.csdn.net/liaoxingliao/article/details/7168500
package l.test1.util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; public class BitmapUtil { private static final Size ZERO_SIZE = new Size(0,0); private static final Options OPTIONS_GET_SIZE = new Options(); private static final Options OPTIONS_DECODE = new Options(); static{ OPTIONS_GET_SIZE.inJustDecodeBounds = true; } public static Size getBitMapSize(String path){ File file = new File(path); if(file.exists()){ InputStream in = null; try{ in = new FileInputStream(file); BitmapFactory.decodeStream(in, null, OPTIONS_GET_SIZE); return new Size(OPTIONS_GET_SIZE.outWidth,OPTIONS_GET_SIZE.outHeight); } catch (FileNotFoundException e) { return ZERO_SIZE; }finally{ closeInputStream(in); } } return ZERO_SIZE; } public static Bitmap createBitmap(String path,int width,int height){ File file = new File(path); if(file.exists()){ InputStream in = null; try { in = new FileInputStream(file); Size size = getBitMapSize(path); if(size.equals(ZERO_SIZE)){ return null; } int scale = 1; int a = size.getWidth() / width; int b = size.getHeight() / height; scale = Math.max(a, b); synchronized (OPTIONS_DECODE) { OPTIONS_DECODE.inSampleSize = scale; Bitmap bitMap = BitmapFactory.decodeStream(in,null,OPTIONS_DECODE); return bitMap; } } catch (FileNotFoundException e) { e.printStackTrace(); } finally{ closeInputStream(in); } } return null; } public static void destory(Bitmap bitmap){ if(null != bitmap && !bitmap.isRecycled()){ bitmap.recycle(); bitmap = null; } } private static void closeInputStream(InputStream in) { if(null != in){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } class Size{ private int width,height; Size(int width,int height){ this.width = width; this.height = height; } public int getWidth() { return width; } public int getHeight() { return height; } }
package l.test1.util; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.Map; import java.util.Set; import java.util.Stack; import android.graphics.Bitmap; /** * Bitmap工具類,緩存用過的指定數量的圖片,使用此工具類,不再需要手動管理Bitmap內存 * 原理: * 用一個隊列保存使用Bitmap的順序,每次使用Bitmap將對象移動到隊列頭 * 當內存不夠,或者達到製定的緩存數量的時候,回收隊列尾部圖片 * 保證當前使用最多的圖片得到最長時間的緩存,提高速度 * @author liaoxingliao * */ public final class BitMapLRU { private static int CACHE_BYTE_SIZE = 10*1024*1024; //緩存10M圖片 private static int CACHE_SIZE = 2000; //緩存圖片數量 private static int byteSize = 0; private static final byte[] LOCKED = new byte[0]; private static final LinkedList<String> CACHE_ENTRIES = //此對象用來保持Bitmap的回收順序,保證最後使用的圖片被回收 new LinkedList<String>(){ private static final long serialVersionUID = 1L; @Override public void addFirst(String object) { while(remove(object)); super.addFirst(object); } }; private static final Stack<QueueEntry> TASK_QUEUE = new Stack<QueueEntry>(); //線程請求創建圖片的隊列 private static final Set<String> TASK_QUEUE_INDEX = new HashSet<String>(); //保存隊列中正在處理的圖片的key,有效防止重複添加到請求創建隊列 private static final Map<String,Bitmap> IMG_CACHE_INDEX = new HashMap<String,Bitmap>(); //緩存Bitmap 通過圖片路徑,圖片大小 static{ // 初始化創建圖片線程,並等待處理 new Thread(){ {setDaemon(true);} public void run() { while(true) { synchronized (TASK_QUEUE) { if(TASK_QUEUE.isEmpty()) { try { TASK_QUEUE.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } QueueEntry entry = TASK_QUEUE.pop(); String key = createKey(entry.path,entry.width,entry.height); TASK_QUEUE_INDEX.remove(key); createBitmap(entry.path, entry.width, entry.height); } } }.start(); } /** * 創建一張圖片 如果緩存中已經存在,則返回緩存中的圖,否則創建一個新的對象,並加入緩存 * 寬度,高度,為了縮放原圖減少內存的,如果輸入的寬,高,比原圖大,返回原圖 * @param path 圖片物理路徑 (必須是本地路徑,不能是網絡路徑) * @param width 需要的寬度 * @param height 需要的高度 * @return */ public static Bitmap createBitmap(String path, int width, int height) { Bitmap bitMap = null; try{ while(CACHE_ENTRIES.size() >= CACHE_SIZE || byteSize >= CACHE_BYTE_SIZE) { destoryLast(); } bitMap = useBitmap(path, width, height); if( bitMap != null && !bitMap.isRecycled()){ return bitMap;//4294967296 } bitMap = BitmapUtil.createBitmap(path, width, height); if(bitMap == null) { //可能不是有效的圖片.. return null; } byteSize += (bitMap.getRowBytes() * bitMap.getHeight()); String key = createKey(path,width,height); synchronized (LOCKED) { IMG_CACHE_INDEX.put(key, bitMap); CACHE_ENTRIES.addFirst(key); } }catch(OutOfMemoryError err) { System.out.println("OOM:" + byteSize); destoryLast(); return createBitmap(path, width, height); } return bitMap; } /** * 設置緩存圖片數量 如果輸入負數,會產生異常 * @param size */ public static void setCacheSize(int size) { if(size <=0 ){ throw new RuntimeException("size :"+size); } while(size < CACHE_ENTRIES.size()){ destoryLast(); } CACHE_SIZE = size; } /** * 加入一個圖片處理請求到圖片創建隊列 * @param path 圖片路徑(本地) * @param width 圖片寬度 * @param height 圖片高度 */ public static void addTask(String path, int width, int height) { QueueEntry entry = new QueueEntry(); entry.path = path; entry.width = width; entry.height = height; synchronized (TASK_QUEUE) { while(TASK_QUEUE.size() > 20) { QueueEntry e = TASK_QUEUE.lastElement(); TASK_QUEUE.remove(e); TASK_QUEUE_INDEX.remove(createKey(e.path, e.width, e.height)); } String key = createKey(path,width,height); if(!TASK_QUEUE_INDEX.contains(key) && !IMG_CACHE_INDEX.containsKey(key)){ TASK_QUEUE.push(entry); TASK_QUEUE_INDEX.add(key); TASK_QUEUE.notify(); } } } public static void cleanTask() { synchronized (TASK_QUEUE) { TASK_QUEUE_INDEX.clear(); TASK_QUEUE.clear(); } } // 將圖片加入隊列頭 private static Bitmap useBitmap(String path,int width,int height) { Bitmap bitMap = null; String key = createKey(path,width,height); synchronized (LOCKED) { bitMap = IMG_CACHE_INDEX.get(key); if(null != bitMap){ CACHE_ENTRIES.addFirst(key); } } return bitMap; } // 回收最後一張圖片 private static void destoryLast() { synchronized (LOCKED) { if(!CACHE_ENTRIES.isEmpty()) { String key = CACHE_ENTRIES.removeLast(); if(key.length() > 0) { Bitmap bitMap = IMG_CACHE_INDEX.remove(key); if(bitMap != null) { bitMap.recycle(); byteSize -= (bitMap.getRowBytes() * bitMap.getHeight()); bitMap = null; } } } } } // 創建鍵 private static String createKey(String path,int width,int height) { if(null == path || path.length() == 0) { return ""; } return path+"_"+width+"_"+height; } // 隊列緩存參數對象 static class QueueEntry{ public String path; public int width; public int height; } }
最後更新:2017-04-02 16:47:34
上一篇:
java.text.format 將字符串“060503”轉化為06:05:03或者將"20081002102030“轉化為2008-10-02 10:00:30
下一篇:
Android 開發圖片壓縮/縮略圖的方法
C# 數據庫存儲過程的講解應用
電子商務類站點終極資源大全
雲上Docker的Spring Cloud微服務應用實踐分享
windows7用VMware workstation安裝ubuntu server 16.04 虛擬機
swing開發圖形界麵工具配置(可自由拖控件上去)
《Cucumber:行為驅動開發指南》——第6章 Cucumber常見問題及解決之道 6.1感受痛苦
【雲棲你的美】pk十10交流6747·777群
lucene join解決父子關係索引
Hadoop 這樣業界頂級的大規模數據處理平台,均發現滿足不了類似雙十一這樣全世界的剁手黨蜂擁而至的熱情
滿江紅開放技術研究組織發布Seam 2.0中文文檔RC版