如今的高清大圖,動輒就要好幾M,而Android對單個應用所施加的內存限制,只有
小几十M,如16M,這致使加載Bitmap
的時候很容易出現內存溢出。以下異常信
息,即是在開發中常常須要的:java
java.lang.OutofMemoryError:bitmap size exceeds VM budgetgit
爲了解決這個問題,就出現了Bitmap的高效加載策略。其實核心思想很簡單。假設
經過ImageView
來顯示圖片,不少時候ImageView
並無原始圖片的尺寸那麼大,
這個時候把整個圖片加載進來後再設置給ImageView
,顯然是沒有必要的,由於ImageView
根本沒辦法顯示原始圖片。這時候就能夠按必定的採樣率來將圖片縮小
後再加載進來,這樣圖片既能在ImageView
顯示出來,又能下降內存佔用從而在一
定程度上避免OOM
,提升了Bitmap
加載時的性能github
Bitmap在Android中指的是一張圖片。經過BitmapFactory類提供的四類方法:decodeFile
,decodeResource
,decodeStream
和decodeByteArray
,分別從文件系統,
資源,輸入流和字節數組中加載出一個Bitmap對象,其中decodeFile,decodeResource
又間接調用了decodeStream
方法,這四類方法最終是
在Android的底層實現的,對應着BitmapFactory
類的幾個native方法。數組
BitmapFactory.Options
的參數①inSampleSize
參數
上述四類方法都支持BitmapFactory.Options
參數,而Bitmap
的按必定採樣率進行縮
放就是經過BitmapFactory.Options
參數實現的,主要用到了inSampleSize
參數,即
採樣率。經過對inSampleSize
的設置,對圖片的像素的高和寬進行縮放。ide
當inSampleSize=1
,即採樣後的圖片大小爲圖片的原始大小。小於1,也按照1來計
算。 當inSampleSize>1
,即採樣後的圖片將會縮小,縮放比例爲1/(inSampleSize
的二次方)。性能
例如: 一張1024 ×1024像素的圖片,採用ARGB8888格式存儲,那麼內存大小
1024×1024×4=4M。若是inSampleSize=2
,那麼採樣後的圖片內存大小:
512×512×4=1M。code
注意: 官方文檔支出,inSampleSize
的取值應該老是2的指數,如1,2,4,8等。
若是外界傳入的inSampleSize
的值不爲2的指數,那麼系統會向下取整並選擇一個
最接近2的指數來代替。好比3,系統會選擇2來代替。當時經驗證實並不是在全部
Android版本上都成立。對象
關於inSampleSize
取值的注意事項: 一般是根據圖片寬高實際的大小/須要的寬高
大小,分別計算出寬和高的縮放比。但應該取其中最小的縮放比,避免縮放圖片太
小,到達指定控件中不能鋪滿,須要拉伸從而致使模糊。圖片
例如: ImageView
的大小是100×100像素,而圖片的原始大小爲200×300,那麼寬
的縮放比是2,高的縮放比是3。若是最終inSampleSize=2
,那麼縮放後的圖片大小
100×150,仍然合適ImageView
。若是inSampleSize=3
,那麼縮放後的圖片大小小
於ImageView
所指望的大小,這樣圖片就會被拉伸而致使模糊。ip
②inJustDecodeBounds
參數
咱們須要獲取加載的圖片的寬高信息,而後交給inSampleSize
參數選擇縮放比縮
放。那麼如何能先不加載圖片卻能得到圖片的寬高信息,經過inJustDecodeBounds=true
,而後加載圖片就能夠實現只解析圖片的寬高信息,並
不會真正的加載圖片,因此這個操做是輕量級的。當獲取了寬高信息,計算出縮放
比後,而後在將inJustDecodeBounds=false
,再從新加載圖片,就能夠加載縮放後
的圖片。
注意: BitmapFactory
獲取的圖片寬高信息和圖片的位置以及程序運行的設備有
關,好比同一張圖片放在不一樣的drawable
目錄下或者程序運行在不一樣屏幕密度的設
備上,均可能致使BitmapFactory
獲取到不一樣的結果,和Android的資源加載機制
有關
Bitmap
的流程①將BitmapFactory.Options
的inJustDecodeBounds
參數設爲true並加載圖片。
②從BitmapFactory.Options
中取出圖片的原始寬高信息,它們對應於outWidth
和outHeight
參數。
③根據採樣率的規則並結合目標View的所需大小計算出採樣率inSampleSize
。
④將BitmapFactory.Options
的inJustDecodeBounds
參數設爲false,而後從新加載
圖片。
Bitmap
高效加載的代碼實現public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight){ BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; //加載圖片 BitmapFactory.decodeResource(res,resId,options); //計算縮放比 options.inSampleSize = calculateInSampleSize(options,reqHeight,reqWidth); //從新加載圖片 options.inJustDecodeBounds =false; return BitmapFactory.decodeResource(res,resId,options); } private static int calculateInSampleSize(BitmapFactory.Options options, int reqHeight, int reqWidth) { int height = options.outHeight; int width = options.outWidth; int inSampleSize = 1; if(height>reqHeight||width>reqWidth){ int halfHeight = height/2; int halfWidth = width/2; //計算縮放比,是2的指數 while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){ inSampleSize*=2; } } return inSampleSize; }
這個時候就能夠經過以下方式高效加載圖片:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.mipmap.ic_launcher,100,100);
除了BitmapFactory
的decodeResource
方法,其餘方法也能夠相似實現。
更多內容詳情請關注個人GitHub:https://github.com/xiangjiana/Android-MS