一般行動裝置像是手機, 記憶體(RAM)是有限的, 不像PC上可以動不動就有2G以上的記憶體可用, 既然是這樣, 在手機上就不可能任意隨我們揮霍記憶體, 在Android上, 對每個程式能使用的記憶體也有其限制, 每隻程式, 能用的Java heap, 除非手機廠商有特別改過, 要不然HVGA的裝置一支程式就只能使用16MB的Java Heap, WVGA則只能使用32MB
通常佔用記憶體一個很大的元兇就是圖, 圖在載入顯示後, 在記憶體中是未經壓縮的Bitmap, 所以佔用了相當大的heap空間,而且在開發初期不太容易注意到這問題, 除非開發初期你就是碰巧拿到一張很大的圖擋(比如說千萬像素級相機拍的照片, 一般手機尚未有千萬以上等級, 所以單用手機拍的照片, 還真不容易試到問題)
當然, 把圖片縮小就是一個最直覺的解法
翻了API doc你可能會發現Bitmap裡面有個method是用來做rescale bitmap用的:
public static Bitmap createScaledBitmap (Bitmap src, int dstWidth, int dstHeight, boolean filter)
但其實, 這method並不適用節省memory這目的, 他本身有個陷阱, 也就是你必須要先載入一張原始圖, 才能針對這張圖檔做rescale的動作, 所以除了原始圖外還會有一份縮好的圖檔, 反而更佔空間
正確的解法是, 你必須要從檔案載入時就已經先把它縮小了,
//Only decode image size. Not whole image
BitmapFactory.Options option = new BitmapFactory.Options();
option.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imagePath, option);
//The new size to decode to
final int NEW_SIZE=100;
//Now we have image width and height. We should find the correct scale value. (power of 2)
int width=option.outWidth;
int height=option.outHeight;
int scale=1;
while(true){
if(width/2<NEW_SIZE || height/2<NEW_SIZE)
break;
width/=2;
height/=2;
scale++;
}
//Decode again with inSampleSize
option = new BitmapFactory.Options();
option.inSampleSize=scale;
return BitmapFactory.decodeFile(imagePath, option);
BitmapFactory可以在載入圖時指定scale的比例, 這樣載入後就不會是原先的大圖了, 缺點是, 要decode兩次, 第一次要使用option.inJustDecodeBounds = true; 讓它不是decode整張圖而是只取得圖的寬高, 第二次才是真正把整張圖decode放到記憶體內, 第二個缺點是, 它的scale算法是以1/2的次方, 沒辦法剛好是你指定的大小
如果把這方式跟createScaledBitmap加起來應用, 那就可以在不造成OutOfMemoryError狀況下, 又可以取得一張符合程式需要顯示大小的圖片了
好強
回覆刪除