2012-03-30 8 views
1

Androidのグリッドビューに非常に小さな画像(平均サイズは90kb)を読み込もうとしています。私は9以上の画像をロードするたびに、私はメモリの問題が発生しています。私は画像をより小さなサイズにスケーリングしようとしましたが、これはある程度までは機能しますが、画質がひどいため実際には真の解決策ではありません。Android OutOfMemoryErrorがGridViewのビットマップをロードするとき

コードが

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

の2点を下回っている:90キロバイト圧縮された画像のサイズは本当に重要ではありません

  1. 。メモリ使用量はビットマップの実際の解像度(この場合は300 * 300 * 4bpp)によって決まります。ビットマップあたり約360kです。

  2. ジンジャーブレッドには、ガベージコレクションが同時に発生するという事実と組み合わされた(Javaヒープ上ではなく)ネイティブアレイにビットマップメモリ​​が格納されていることが原因でいくつかの欠陥があります。この事実のために、ビットマップメモリ​​を再利用できることを認識するために、メモリマネージャーが長くなることがあります。

ので、これの波及効果は、以下のとおりです。メモリ使用量を推定する際

  1. は、実際の解凍ビットマップのサイズを考えてみましょう。
  2. できるだけ中間のビットマップをリサイクルして、メモリをより速く回収できるようにします。たとえば、ビットマップをスケーリングする場合は、スケーリングされたソースへの参照を保存し、スケーリングの完了後にソースをリサイクルします(スケーリングの結果と比較して、同じビットマップが返される可能性があるため)。

可能であれば、コードをICSデバイスでテストしてください。ヒープ検査ツールを使用して、最も多くのメモリが使用されている場所を知ることができます。 Sine ICSはJavaヒープ上にビットマップメモリ​​を割り当てますので、ビットマップメモリ​​の使用状況を正確に把握できます。

+0

ご回答いただきありがとうございます。ビットマップのリサイクルを試みましたが、リサイクルされたビットマップを使用しようとしているという例外が続いています。ビットマップをリサイクルする必要がある例をお勧めします。 – JoshDavies

+0

ビットマップをリサイクルするのは難しいです。ビューにまだ接続されていないインスタンスのみをリサイクルするようにしてください。あなたはジンジャーブレッドでこれらの問題を経験していますか?それがGinberbreadでない場合、リサイクルに関する私の提案は助けになる可能性が低く、ビットマップを何らかの形で漏らしている可能性が高いです。漏洩した文脈はしばしばここでの犯人です。そのような場合は、ヒープトレースツールを使用すると、コンテキストが長時間にわたって有効かどうかを判断するのに役立ちます。あなたがビットマップのArrayListを保持しているという事実は、問題である可能性が高いことを意味します。 –

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

+0

また、このコメントは間違っています。ある時点で、ImageViewは画像を解凍して表示する必要があるため、同じ量のメモリが最後に使用されます。 –

関連する問題