android中BitmapFactory.Options的使用是在載入圖片時,就從圖片的載入和使用說起
怎樣擷取圖片的大小?
首先我們把這個圖片轉成Bitmap,然後再利用Bitmap的getWidth()和getHeight()方法就可以取到圖片的寬高了。
新問題又來了,在通過BitmapFactory.decodeFile(String path)方法將突破轉成Bitmap時,遇到大一些的圖片,我們經常會遇到OOM(Out Of Memory)的問題。怎麼避免它呢?
這就用到了我們上面提到的BitmapFactory.Options這個類。
BitmapFactory.Options這個類,有一個欄位叫做 inJustDecodeBounds 。SDK中對這個成員的說明是這樣的:
If set to true, the decoder will return null (no bitmap), but the out…
也就是說,如果我們把它設為true,那麼BitmapFactory.decodeFile(String path, Options opt)並不會真的返回一個Bitmap給你,它僅僅會把它的寬,高取回來給你,這樣就不會佔用太多的記憶體,也就不會那麼頻繁的發生OOM了。
範例程式碼如下:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(path, options);/* 這裡返回的bmp是null */
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bmp = BitmapFactory.decodeFile(path, options);/* 這裡返回的bmp是null */
這段代碼之後,options.outWidth 和 options.outHeight就是我們想要的寬和高了。
有了寬,高的資訊,我們怎樣在圖片不變形的情況下擷取到圖片指定大小的縮圖呢?
比如我們需要在圖片不變形的前提下得到寬度為200的縮圖。
那麼我們需要先計算一下縮放之後,圖片的高度是多少 ,代碼如下
int height = options.outHeight * 200 / options.outWidth;
options.outWidth = 200;
options.outHeight = height;
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
image.setImageBitmap(bmp);
int height = options.outHeight * 200 / options.outWidth;
options.outWidth = 200;
options.outHeight = height;
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
image.setImageBitmap(bmp);
這樣雖然我們可以得到我們期望大小的ImageView
但是在執行BitmapFactory.decodeFile(path, options);時,並沒有節約記憶體。要想節約記憶體,還需要用到BitmapFactory.Options這個類裡的 inSampleSize 這個成員變數。
我們可以根據圖片實際的寬高和我們期望的寬高來計算得到這個值。
options.inSampleSize = options.outWidth / 200;
/*圖片長寬方向縮小倍數*
options.inSampleSize = options.outWidth / 200;
/*圖片長寬方向縮小倍數*/
另外,為了節約記憶體我們還可以使用下面的幾個欄位:
options.inDither=false;/*不進行圖片抖動處理*/
options.inPreferredConfig=null; /*設定讓解碼器以最佳方式解碼*/
/* 下面兩個欄位需要組合使用 */
options.inPurgeable = true;
options.inInputShareable = true;
android的BitmapFactory.Options避免記憶體溢出OOM的最佳化方法
盡量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource來設定一張大圖,
因為這些函數在完成decode後,最終都是通過java層的createBitmap來完成的,需要消耗更多記憶體。
因此,改用先通過BitmapFactory.decodeStream方法,建立出一個bitmap,再將其設為ImageView的 source,
decodeStream最大的秘密在於其直接調用JNI>>nativeDecodeAsset()來完成decode,
無需再使用java層的createBitmap,從而節省了java層的空間。
如果在讀取時加上圖片的Config參數,可以跟有效減少載入的記憶體,從而跟有效阻止拋out of Memory異常
另外,decodeStream直接拿的圖片來讀取位元組碼了, 不會根據機器的各種解析度來自動適應,
使用了decodeStream之後,需要在hdpi和mdpi,ldpi中配置相應的圖片資源,
否則在不同解析度機器上都是同樣大小(像素點數量),顯示出來的大小就不對了。
另外,以下方式也大有協助:
1. InputStream is = this.getResources().openRawResource(R.drawable.pic1);
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds = false;
options.inSampleSize = 10; //width,hight設為原來的十分一
Bitmap btp =BitmapFactory.decodeStream(is,null,options);
2. if(!bmp.isRecycle() ){
bmp.recycle() //回收圖片所佔的記憶體
system.gc() //提醒系統及時回收
}
以下奉上一個方法:
Java代碼
/** * 以最省記憶體的方式讀取本地資源的圖片 * @param context * @param resId * @return */ public static Bitmap readBitMap(Context context, int resId){ BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; //擷取資源圖片 InputStream is = context.getResources().openRawResource(resId); return BitmapFactory.decodeStream(is,null,opt); }
最佳化Dalvik虛擬機器的堆記憶體配置
對 於Android平台來說,其託管層使用的Dalvik JavaVM從目前的表現來看還有很多地方可以最佳化處理,比如我們在開發一些大型遊戲或耗資源的應用中可能考慮手動幹涉GC處理,使用 dalvik.system.VMRuntime類提供的setTargetHeapUtilization方法可以增強程式堆記憶體的處理效率。當然具體 原理我們可以參考開源工程,這裡我們僅說下使用方法: private final static floatTARGET_HEAP_UTILIZATION = 0.75f; 在程式onCreate時就可以調用 VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 即可。
介紹一下圖片佔用進程的記憶體演算法吧。
android中處理圖片的基礎類是Bitmap,顧名思義,就是位元影像。佔用記憶體的演算法如下:
圖片的width*height*Config。
如果Config設定為ARGB_8888,那麼上面的Config就是4。一張480*320的圖片佔用的記憶體就是480*320*4 byte。
前面有人說了一下8M的概念,其實是在預設情況下android進程的記憶體佔用量為16M,因為Bitmap他除了java中持有資料外,底層C++的 skia圖形庫還會持有一個SKBitmap對象,因此一般圖片佔用記憶體推薦大小應該不超過8M。這個可以調整,編譯原始碼時可以設定參數。