OutOfMemoryErrorは要するに「メモリを使いすぎです」、というエラーです。
BitmapFactoryで画像を沢山使っていると結構頻発します、私はコレに結構苦戦しました:(;゙゚'ω゚'):
対策の一つとして、カメラ機能を使って撮影した画像などを使う時に縮小してから読込みをする方法があります、
フルサイズで撮影した画像はサイズがディスプレイの解像度の何杯もあったりします。
せっかく実寸で読み込んでも結局表示する時には縮小してしまうのなら、読込みの際に縮小してしまおうという試みです。
サンプルなどを紹介してみます(^ω^)
撮影した画像をそのまま読み込んでOutOfMemoryErrorを発生させる
SDカードにフルサイズで撮影した画像を読み込んで何枚目でOutOfMemoryErrorが発生するか調べるプログラムです。
実行すると、2枚目でOutOfMemoryErrorが発生していまいます。だめぽ。
String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath(); try{ TextView tv1 = new TextView(this); tv1.setText("1枚目読込開始"); ll.addView(tv1); Bitmap bmp1 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0222.jpg"); TextView tv2 = new TextView(this); tv2.setText("2枚目読込開始"); ll.addView(tv2); Bitmap bmp2 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0223.jpg"); TextView tv3 = new TextView(this); tv3.setText("3枚目読込開始"); ll.addView(tv3); Bitmap bmp3 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0225.jpg"); }catch(OutOfMemoryError e){ TextView tvErr = new TextView(this); tvErr.setText("OutOfMemoryError発生"); ll.addView(tvErr); }
【実行結果】
BitmapFactory.Optionsを使って画像のサイズ取得、縮小指定をしてから読み込む
読み込み時の設定を行うには、BitmapFactory.decodeFileの引数にBitmapFactory.Optionsを指定します。
まず、BitmapFactory.OptionsのinJustDecodeBoundsをtrueすると、
実際には画像を読み込まずサイズだけを取得できます。
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; Bitmap bmp1 = BitmapFactory.decodeFile(【画像パス】, options );
読み込んだサイズはoptions.outWidthとoptions.outHeightに格納されるので、ディスプレイのサイズと比較して比率を計算できます。
int scaleW = options.outWidth /【画面横サイズ】; int scaleH = options.outHeight / 【画面縦サイズ】; int scale = Math.max(scaleW, scaleH);
比率を計算できたら、その値をoptions.inSampleSizeに設定し、
inJustDecodeBoundsを今度はfalseにして再び読込み、これで縮小したサイズで読込みができました。
options.inSampleSize = scale; options.inJustDecodeBounds = false; //設定を戻す bmp1 = BitmapFactory.decodeFile(【画像パス】, options );
先ほどのサンプルプログラムを修正した例です。
String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath(); //読み込み用のオプションオブジェクトを生成 BitmapFactory.Options options = new BitmapFactory.Options(); // ディスプレイのサイズを取得 WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE); Display disp = wm.getDefaultDisplay(); int scaleW,scaleH,scale; try{ TextView tv1 = new TextView(this); tv1.setText("1枚目読込開始"); ll.addView(tv1); options.inJustDecodeBounds = true; //画像のサイズ情報だけを取得する設定 Bitmap bmp1 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0222.jpg", options ); scaleW = options.outWidth / disp.getWidth(); scaleH = options.outHeight / disp.getHeight(); scale = Math.max(scaleW, scaleH); options.inSampleSize = scale; //縮尺値を指定 options.inJustDecodeBounds = false; //設定を戻す bmp1 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0222.jpg", options ); TextView tv2 = new TextView(this); tv2.setText("2枚目読込開始"); ll.addView(tv2); options.inJustDecodeBounds = true; //画像のサイズ情報だけを取得する設定 Bitmap bmp2 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0223.jpg", options ); scaleW = options.outWidth / disp.getWidth(); scaleH = options.outHeight / disp.getHeight(); scale = Math.max(scaleW, scaleH); options.inSampleSize = scale; //縮尺値を指定 options.inJustDecodeBounds = false; //設定を戻す bmp2 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0223.jpg", options ); TextView tv3 = new TextView(this); tv3.setText("3枚目読込開始"); ll.addView(tv3); options.inJustDecodeBounds = true; //画像のサイズ情報だけを取得する設定 Bitmap bmp3 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0225.jpg", options ); scaleW = options.outWidth / disp.getWidth(); scaleH = options.outHeight / disp.getHeight(); scale = Math.max(scaleW, scaleH); options.inSampleSize = scale; //縮尺値を指定 options.inJustDecodeBounds = false; //設定を戻す bmp3 = BitmapFactory.decodeFile(sdPath+"/DCIM/101SHARP/DSC_0225.jpg", options ); }catch(OutOfMemoryError e){ TextView tvErr = new TextView(this); tvErr.setText("OutOfMemoryError発生"); ll.addView(tvErr); }
【実行結果】
今度は3枚とも読込みができました(^ω^)