Bitmap內存管理

內存複用

Bitmap內存管理

複用之前

Bitmap內存管理

複用之後

代碼

 BitmapFactory.Options options=new BitmapFactory.Options();
//如果要複用,需要設計成異變
\t\toptions.inMutable=true;
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.mipmap.wyz_p,options);
for(int i=0;i<100;i++){
options.inBitmap=bitmap;
bitmap=BitmapFactory.decodeResource(getResources(),R.mipmap.wyz_p,options);
}

設計緩存

申請圖片--》內存緩存--》(內存緩存放不下了)複用池--》磁盤緩存--》網絡找

其中緩存都採用的是LRU算法

內存緩存使用系統提供的LRUCache,磁盤緩存需要下載第三方庫DiskLruCache

先定義一個圖片緩存管理類,ImageCache.java

/**
* 管理內存中的圖片
*/
public class ImageCache {
private static ImageCache instance;
private Context context;
private LruCache<string> memoryCache;
private DiskLruCache diskLruCache;
BitmapFactory.Options options=new BitmapFactory.Options();
/**
* 定義一個複用沲
*/
public static Set<weakreference>> reuseablePool;
public static ImageCache getInstance(){

if(null==instance){
synchronized (ImageCache.class){
if(null==instance){
instance=new ImageCache();
}
}
}
return instance;
}
//引用隊列
ReferenceQueue referenceQueue;
Thread clearReferenceQueue;
boolean shutDown;
private ReferenceQueue<bitmap> getReferenceQueue(){
if(null==referenceQueue){
//當弱用引需要被回收的時候,會進到這個隊列中
referenceQueue=new ReferenceQueue<bitmap>();
//單開一個線程,去掃描引用隊列中GC掃到的內容,交到native層去釋放
clearReferenceQueue=new Thread(new Runnable() {
@Override
public void run() {
while(!shutDown){
try {
//remove是阻塞式的
Reference<bitmap> reference=referenceQueue.remove();
Bitmap bitmap=reference.get();
if(null!=bitmap && !bitmap.isRecycled()){
bitmap.recycle();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
clearReferenceQueue.start();
}
return referenceQueue;
}
//dir是用來存放圖片文件的路徑
public void init(Context context,String dir){
this.context=context.getApplicationContext();
//複用池

reuseablePool=Collections.synchronizedSet(new HashSet<weakreference>>());
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//獲取程序最大可用內存 單位是M
int memoryClass=am.getMemoryClass();
//參數表示能夠緩存的內存最大值 單位是byte
memoryCache=new LruCache<string>(memoryClass/8*1024*1024){
/**
* @return value佔用的內存大小
*/
@Override
protected int sizeOf(String key, Bitmap value) {
//19之前 必需同等大小,才能複用 inSampleSize=1
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT){
return value.getAllocationByteCount();
}
return value.getByteCount();
}
/**
* 當lru滿了,bitmap從lru中移除對象時,會回調
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if(oldValue.isMutable()){//如果是設置成能複用的內存塊,拉到java層來管理
//3.0以下 Bitmap native
//3.0以後---8.0之前 java
//8。0開始 native
//把這些圖片放到一個複用池中
reuseablePool.add(new WeakReference<bitmap>(oldValue,referenceQueue));
}else{
//oldValue就是移出來的對象
oldValue.recycle();
}
}
};
//valueCount:表示一個key對應valueCount個文件
try {
diskLruCache = DiskLruCache.open(new File(dir), BuildConfig.VERSION_CODE, 1, 10 * 1024 * 1024);
}catch(Exception e){

e.printStackTrace();
}
getReferenceQueue();
}
public void putBitmapToMemeory(String key,Bitmap bitmap){
memoryCache.put(key,bitmap);
}
public Bitmap getBitmapFromMemory(String key){
return memoryCache.get(key);
}
public void clearMemoryCache(){
memoryCache.evictAll();
}
//獲取複用池中的內容
public Bitmap getReuseable(int w,int h,int inSampleSize){
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
return null;
}
Bitmap reuseable=null;
Iterator<weakreference>> iterator = reuseablePool.iterator();
while(iterator.hasNext()){
Bitmap bitmap=iterator.next().get();
if(null!=bitmap){
//可以複用
if(checkInBitmap(bitmap,w,h,inSampleSize)){
reuseable=bitmap;
iterator.remove();
break;
}else{
iterator.remove();
}
}
}
return reuseable;
}
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
return bitmap.getWidth()==w && bitmap.getHeight()==h && inSampleSize==1;
}
if(inSampleSize>=1){
w/=inSampleSize;
h/=inSampleSize;
}
int byteCount=w*h*getPixelsCount(bitmap.getConfig());
return byteCount<=bitmap.getAllocationByteCount();
}
private int getPixelsCount(Bitmap.Config config) {
if(config==Bitmap.Config.ARGB_8888){
return 4;

}
return 2;
}
//磁盤緩存的處理
/**
* 加入磁盤緩存
*/
public void putBitMapToDisk(String key,Bitmap bitmap){
DiskLruCache.Snapshot snapshot=null;
OutputStream os=null;
try {
snapshot=diskLruCache.get(key);
//如果緩存中已經有這個文件 不理他
if(null==snapshot){
//如果沒有這個文件,就生成這個文件
DiskLruCache.Editor editor=diskLruCache.edit(key);
if(null!=editor){
os=editor.newOutputStream(0);
bitmap.compress(Bitmap.CompressFormat.JPEG,50,os);
editor.commit();
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=snapshot){
snapshot.close();
}
if(null!=os){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 從磁盤緩存中取
*/
public Bitmap getBitmapFromDisk(String key,Bitmap reuseable){
DiskLruCache.Snapshot snapshot=null;
Bitmap bitmap=null;
try {
snapshot=diskLruCache.get(key);

if(null==snapshot){
return null;
}
//獲取文件輸入流,讀取bitmap
InputStream is=snapshot.getInputStream(0);
//解碼個圖片,寫入
options.inMutable=true;
options.inBitmap=reuseable;
bitmap=BitmapFactory.decodeStream(is,null,options);
if(null!=bitmap){
memoryCache.put(key,bitmap);
}
} catch (IOException e) {
e.printStackTrace();
} finally{
if(null!=snapshot){
snapshot.close();
}
}
return bitmap;
}
}
/<weakreference>/<bitmap>/<string>/<weakreference>/<bitmap>/<bitmap>/<bitmap>/<weakreference>/<string>

縮放圖片類ImageResize.java

public class ImageResize {
/**
* 縮放bitmap
* @param context
* @param id
* @param maxW
* @param maxH
* @return
*/
public static Bitmap resizeBitmap(Context context,int id,int maxW,int maxH,boolean hasAlpha,Bitmap reusable){
Resources resources = context.getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
// 只解碼出 outxxx參數 比如 寬、高
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resources,id,options);
//根據寬、高進行縮放
int w = options.outWidth;
int h = options.outHeight;
//設置縮放係數

options.inSampleSize = calcuteInSampleSize(w,h,maxW,maxH);
if (!hasAlpha){
options.inPreferredConfig = Bitmap.Config.RGB_565;
}
options.inJustDecodeBounds = false;
//設置成能複用
options.inMutable=true;
options.inBitmap=reusable;
return BitmapFactory.decodeResource(resources,id,options);
}
/**
* 計算縮放係數
* @param w
* @param h
* @param maxW
* @param maxH
* @return 縮放的係數
*/
private static int calcuteInSampleSize(int w,int h,int maxW,int maxH) {
int inSampleSize = 1;
if (w > maxW && h > maxH){
inSampleSize = 2;
//循環 使寬、高小於 最大的寬、高
while (w /inSampleSize > maxW && h / inSampleSize > maxH){
inSampleSize *= 2;
}
}
return inSampleSize;
}
}

最後使用以上兩個類

 Bitmap bitmap=ImageCache.getInstance().getBitmapFromMemory(String.valueOf(position));
if(null==bitmap){
//如果內存沒數據,就去複用池找
Bitmap reuseable=ImageCache.getInstance().getReuseable(60,60,1);
//reuseable能複用的內存
//從磁盤找
bitmap = ImageCache.getInstance().getBitmapFromDisk(String.valueOf(position),reuseable);
//如果磁盤中也沒緩存,就從網絡下載
if(null==bitmap){

bitmap=ImageResize.resizeBitmap(context,R.mipmap.wyz_p,80,80,false,reuseable);
ImageCache.getInstance().putBitmapToMemeory(String.valueOf(position),bitmap);
ImageCache.getInstance().putBitMapToDisk(String.valueOf(position),bitmap);
}
}


分享到:


相關文章: