Bitmap內存管理一些理解
bitmap內存是存儲在哪里
android 3.0 到7.1 bitmap內存是放在dalvik heap中,3.0以下和 8.0 以后放在native heap中,
Bitmap復用內存
/設置成是否需要復用內存
options.inMutable=true;
options.inBitmap=multiplex;
復用是有根據(jù)不同的Android版本他的復用規(guī)則是不一樣的19之前必須同等大小才能復用inSampleSize=1,19后的只要大小小于等于其大小就可以了。
Bitmap內存復用主要作用是
bitmap復用內存塊,不需要在重新給這個bitmap申請一塊新的內存,避免了一次內存的分配和回收,從而改善了運行效率。
對圖片進行處理成適合我們展示的大小
我們從網(wǎng)絡上下載圖片的時候通常需要對下載的圖片進行縮放,獲取到我們需要使用到的內存圖片,可根據(jù)我們需要展示的寬高來展示。
...
public static Bitmap resizeBitmapResource(Context context,int id,int wI,int hI,boolean hasAlpha,Bitmap multiplex){
Resources resources=context.getResources();
BitmapFactory.Options options=new BitmapFactory.Options();
//拿到圖片的寬高即拿到系統(tǒng)處理的信息
options.inJustDecodeBounds=true;
//我們把原來的解碼參數(shù)改了再生成bitmap
BitmapFactory.decodeResource(resources,id,options);
//取得寬高然后進行縮放
int w=options.outWidth;
int h=options.outHeight;
//設置縮放系數(shù)
options.inSampleSize=setScalingFactor(w,h,wI,hI);
//把系數(shù)恢復過來
options.inJustDecodeBounds=false;
if(!hasAlpha)//是否需要透明度
{
options.inPreferredConfig=Bitmap.Config.RGB_565;//不需要的話我們就用2個字節(jié)即16位即我們減少內存的使用。
}
//設置成是否需要復用內存
options.inMutable=true;
options.inBitmap=multiplex;
return BitmapFactory.decodeResource(resources,id,options);
}
//返回的結果是原來解碼圖片的要縮放的系數(shù)即我們需要顯示的大小,這里找最接近2的幾次方
private static int setScalingFactor(int w, int h, int wI, int hI) {
int inSampleSize=1;
if(w>wI&&h>hI){
inSampleSize=2;
while (w/inSampleSize>wI&&h/inSampleSize>hI){//這個說明還是可以再縮放
inSampleSize*=2;
}
}
return inSampleSize;
}
圖片緩存
通常我們需要把這些圖片緩存起來我們常用的三級緩存
一般是內存緩存和磁盤緩存
基本都是用lru算法lru其內部是LinkedHashMap
那么我來簡單的說一下
lru算法采用的是最近最少使用算法
他的使用規(guī)則是當內存中只能放十張圖片吧,當外面從這十張中獲取的時候假如說是第五張被去出來
那么就將第五張圖片斷開鏈表然后取出來放在隊頭此時第四和第六就連接起來了。
如果是外面的存進來那么這個時候就把外面?zhèn)鬟M來放在隊頭然后把最近最少使用的那一張移出緩存
...
//參數(shù)表示能夠緩存內存最大值,參數(shù)是byte所以10241024
memoryCache= new LruCache<String, Bitmap>(memoryClass/810241024){
@Override
protected int sizeOf(String key, Bitmap value) {
//19之前必須同等大小才能復用inSampleSize=1
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.KITKAT){
return value.getAllocationByteCount(); //為什么要怎么寫就是因為怕計算的存儲數(shù)量錯誤,假如說這個位置每張大小是10m然后總共是100m這樣子的化就只能存10個,假如這個時候有一個比較小的圖片就會造成計算錯誤,因為第一次分配的內存之后每次都要獲得這么大的內存
}
return value.getByteCount();
}
/
表示lru已經放不下了,bitmap從lru中移除對象時,會回調
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if(oldValue.isMutable()){//如果是設置成能復用的內存塊,
//把這些圖片放到一個復用池中
reuseablePool.add(new WeakReference<Bitmap>(oldValue,mReferenceQueue));
}
else{
//oldValue就是移出來的對象
oldValue.recycle();
}
}
};
...
...
public static Set<WeakReference<Bitmap>> reuseablePool;//這個復用池有兩個作用一個是獲取內存,一個是通知要手動回收了
當弱引用被GC掃描到的數(shù)據(jù)會放到mReferenceQueue隊列中這個時候外面就可以手動回收內存
private ReferenceQueue<Bitmap> getReferenceQueue(){
if(mReferenceQueue==null){
//當弱引用需要被回收的時候,會進入到這個隊列中去
mReferenceQueue=new ReferenceQueue<Bitmap>();
//開一個線程去獲取弱引用需要被回收的數(shù)據(jù)即被GC掃描到的數(shù)據(jù),交到native去釋放
clearReferenceQueue=new Thread(new Runnable() {
@Override
public void run() {
while (!shutDown){
try {
Reference<Bitmap> bitmapReferenceQueue= mReferenceQueue.remove();
Bitmap bitmap= bitmapReferenceQueue.get();
if(bitmap!=null&&!bitmap.isRecycled()){
bitmap.recycle();//就是不在等代gc掃描回收而是直接手動調用回收
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
clearReferenceQueue.start();
}
return mReferenceQueue;
}
...
這兩段代碼中注釋已經說的很清楚了
當lru滿的時候在增加新圖片就會移除里面最近最少使用的圖片,這個時候就會回調entryRemoved
在這個方法中首先判斷是否我們設置成能復用的內存塊
如果設置了我們就存到復用池中去,這個復用池有兩個作用一個是獲取內存,一個是通知要手動回收了
那么這個復用池內存是在什么時候用了其是在我們在內存緩存中找不到要用的圖片的時候接著去磁盤緩存中查找或者網(wǎng)絡中下載,
這樣可以避免了一次內存的分配和回收,以及從新分配.
...
Bitmap bitmap=null;
/*
獲取復用的內存
*/
public Bitmap getMultiplex(int w,int h,int inSampleSize){
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.HONEYCOMB){
return null;
}
Bitmap multiplex=null;
Iterator<WeakReference<Bitmap>> iterator=reuseablePool.iterator();
while (iterator.hasNext()){
bitmap=iterator.next().get();
if(bitmap!=null){
if(checkInBitmap(bitmap,w,h,inSampleSize)){
multiplex=bitmap;
}
iterator.remove();
}
}
return multiplex;
}
private boolean checkInBitmap(Bitmap bitmap, int w, int h, int inSampleSize) {
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT){//主要是因為19之前的版本他的復用規(guī)則是要一樣大小并且縮放比是一樣的
return w==bitmap.getWidth()&&h==bitmap.getHeight()&&inSampleSize==1;
}
if(inSampleSize>1){
w/=inSampleSize;
h/=inSampleSize;
}
int byteCount=w*h*getPixelsCount(bitmap.getConfig());
return byteCount<bitmap.getAllocationByteCount();//只要內存比復用的內存小就行了
}
//一般我們都會設置成 Bitmap.Config.RGB_565;
private int getPixelsCount(Bitmap.Config config) {
if(config==Bitmap.Config.ARGB_8888){
return 4;
}
return 2;
}
...
這個主要是獲取復用內存的