2012-03-30 84 views
1

我正試圖在Android中將一些非常小的圖像(平均大小爲90kb)加載到gridview中。每當我加載超過9個圖像,然後我收到內存問題。我試圖將圖像縮小到較小的尺寸,儘管這在一定程度上起作用,但它不是真正的解決方案,因爲圖像質量很差。加載GridView位圖時出現Android OutOfMemoryError

的代碼如下

private Context mContext; 
private ArrayList<Bitmap> photos = new ArrayList<Bitmap>(); 
public Bitmap [] mThumbIds; 

public ImageAdapter(Context c) { 
    mContext = c; 
} 


public Object getItem(int position) { 
    return null; 
} 

public long getItemId(int position) { 
    return 0; 
} 

public Bitmap scaleBitmap(String imagePath) { 
    Bitmap resizedBitmap = null; 
    try { 
     int inWidth = 0; 
     int inHeight = 0; 

     InputStream in; 

     in = new FileInputStream(imagePath); 

     // decode image size (decode metadata only, not the whole image) 
     BitmapFactory.Options options = new BitmapFactory.Options(); 
     options.inJustDecodeBounds = true; 
     BitmapFactory.decodeStream(in, null, options); 
     in.close(); 
     in = null; 

     // save width and height 
     inWidth = options.outWidth; 
     inHeight = options.outHeight; 

     // decode full image pre-resized 
     in = new FileInputStream(imagePath); 
     options = new BitmapFactory.Options(); 
     // calc rought re-size (this is no exact resize) 
     options.inSampleSize = Math.max(inWidth/300, inHeight/300); 
     // decode full image 
     Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options); 

     // calc exact destination size 
     Matrix m = new Matrix(); 
     RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight()); 
     RectF outRect = new RectF(0, 0, 300, 300); 
     m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER); 
     float[] values = new float[9]; 
     m.getValues(values); 

     // resize bitmap 
     resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true); 

    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    return resizedBitmap; 
} 


public void populateGrid() { 
    File sdDir = new File("mnt/sdcard/Pictures"); 
    File[] sdDirFiles = sdDir.listFiles(); 
    for(File singleFile : sdDirFiles) { 
     String filePath = singleFile.getAbsolutePath(); 
     Bitmap bmp = scaleBitmap(filePath); 
     photos.add(bmp); 
    } 
    mThumbIds = photos.toArray(new Bitmap[(photos.size())]); 
} 

// create a new ImageView for each item referenced by the Adapter 
public View getView(int position, View convertView, ViewGroup parent) { 
    ImageView imageView; 
    if (convertView == null) { // if it's not recycled, initialize some attributes 
     imageView = new ImageView(mContext); 
     imageView.setLayoutParams(new GridView.LayoutParams(85, 85)); 
     imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 
     imageView.setPadding(8, 8, 8, 8); 
    } else { 
     imageView = (ImageView) convertView; 
    } 
    imageView.setImageBitmap(mThumbIds[position]); 
    return imageView; 
} 

@Override 
public int getCount() { 
    return mThumbIds.length; 
} 
} 

回答

2

兩個方面的考慮:

  1. 的90KB壓縮圖像大小並不重要。內存的使用取決於位圖的實際分辨率 - 在這種情況下,爲300 * 300 * 4bpp,因此每個位圖約爲360k。

  2. 由於位圖內存存儲在本地數組(而不是Java堆中)以及垃圾收集同時發生的事實,薑餅有一些缺陷。由於這個事實,有時需要更長的內存管理器才能意識到可以重新使用位圖內存。

所以,這樣做的後果是:

  1. 估計內存使用情況時,要考慮實際的解壓縮位圖的大小。
  2. 儘可能地回收中間位圖,以幫助更快地回收內存。例如,如果您要縮放位圖,請保存對預縮放源的引用,並在縮放完成後回收源(與縮放結果進行比較,因爲可能會返回相同的位圖)。

如果可以,請在ICS設備上測試您的代碼。然後,您可以使用堆檢查工具來了解大部分內存在哪裏使用。正弦ICS在Java堆上分配位圖內存,您將獲得位圖內存使用情況的準確圖片。

+0

非常感謝您的回覆。我試圖回收位圖,但不斷收到一個異常,說我試圖使用回收的位圖。你能不能推薦一個我應該回收位圖的例子。 – JoshDavies 2012-03-30 12:29:49

+0

不得不回收位圖很棘手。確保您只回收尚未連接到View的實例。你在薑餅上遇到過這些問題嗎?如果它不是Ginberbread,那麼我對回收的建議就不太可能有幫助,並且更可能是您以某種方式簡單地泄漏Bitmaps。泄漏的上下文通常是這裏的罪魁禍首。如果是這種情況,使用堆跟蹤工具將幫助您確定上下文是否長時間保持活動狀態。事實上,你持有一個BitList的ArrayList意味着這是一個很好的機會。 – 2012-03-30 18:52:19

1

位圖使用大量內存,如果您已經將圖像保存到內存中,則只需使用文件的路徑並將其推送到ImaveView上即可。

ImageView img = new ImageView(getApplicationContext());; 

img.setImageBitmap(BitmapFactory.decodeFile(media.getThumbPath())); 
img.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 

myLinearLayout.addView(img); 

像這樣的東西可能會更好。這樣你就不會把所有的位圖存儲到你的堆中。

+0

感謝您的回覆。我實際上不能這樣做,因爲位圖是從SD卡動態創建和讀取的,因此它們不能存儲爲可繪製的。 – JoshDavies 2012-03-30 12:31:29

+0

此外,此評論不正確。在某些時候,ImageView還必須解壓縮圖像並顯示它,因此最終將使用相同數量的內存。 – 2012-03-30 18:49:13

相關問題