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版