ラベル

ラベル 画像表示 の投稿を表示しています。 すべての投稿を表示
ラベル 画像表示 の投稿を表示しています。 すべての投稿を表示

2011年11月20日日曜日

画像を画面サイズに合わせる(縮尺・縮小する)の続き【android】【画像表示】【エラー】

以前の投稿の続きです。↓
⇒画像を画面サイズに合わせる(縮尺・縮小する)【android】【画像表示】【エラー】

以前、紹介した画像を画面サイズに合わせる方法ですが、いざ、自分で使ってみると
GC(ガーベッジコレクション)が画像毎に発生して、処理が非常に遅いという問題
が発生しました。
※画像表示処理もそこそこに遅いのに、GCがさらに悪化させているようでした。

なんとかGCの発生を抑制したくいろいろ調べた結果、Bitmap作成処理を下記のように
書き換えることで、GCの発生を抑え、処理時間を短くすることができました。

以前の方法では、画像をそのままBitmapに読み込んでから縮尺していたようで、
メモリにごみができやすいという問題があったようです。
今回は画像のサイズをあらかじめ読み込んでおき、画像の読み込みのタイミングで
縮尺しているので、メモリを無駄なく使用することができます。

あと、不本意ではあるのですが、androidでは短命なオブジェクトや変数を生成しない
ようにしたほうがパフォーマンスがあがるという情報を得て、できるだけクラス変数
で、変数を宣言するようにしています。
※ほかにも下記のようなパフォーマンス対策がありました。
  ・setterやgetterは使用しない。
  ・オブジェクトを作成しない(コードはベタ書きすべき)。
  ・処理が遅くなっても問題ない箇所でGCするようにする。
  ・定数はfinalで宣言する。

ただし、そのままこの情報を受け取ってコーディングをすると、非常に見づらい
コードとなってしまいそうなので何か対策を立てる必要がありそうです。


    //setBitmap変数
Bitmap bmImage;
int zoomWidth;
int zoomHeight;

/**
* ディレクトリリストに表示するBitmapを設定する。
* @param File file(設定するBitmapファイル)
* @return Bitmap bmImage(設定されたBitmap)
* @exception なし
*/

public Bitmap setBitmap(File file){
// ビットマップ作成オブジェクトの設定
BitmapFactory.Options bmfOptions = new BitmapFactory.Options();
// RGBでそれぞれ 5/6/5 ビットの色を使用(メモリ対策)
bmfOptions.inPreferredConfig = Config.RGB_565;
// アプリケーション内でのピクセル密度を設定
bmfOptions.inScreenDensity = DisplayMetrics.DENSITY_LOW;
// 画像の大きさだけ取得
bmfOptions.inJustDecodeBounds = true;
bmImage = BitmapFactory.decodeFile(file.getPath(),bmfOptions);

// 画面のサイズの1/4で画像を縮尺するよう、縮尺率を設定
if (displayMetrics != null) {
zoomWidth = (int)Math.floor(bmfOptions.outWidth / (displayMetrics.widthPixels / REDUCTION_SIZE));
zoomHeight = (int)Math.floor(bmfOptions.outHeight / (displayMetrics.heightPixels / REDUCTION_SIZE));
bmfOptions.inSampleSize = Math.max(zoomWidth, zoomHeight);
}
// 画像そのものを設定された縮尺率で取得
bmfOptions.inJustDecodeBounds = false;
bmImage = BitmapFactory.decodeFile(file.getPath(),bmfOptions);

return bmImage;
}
}

2011年11月7日月曜日

画像を画面サイズに合わせる(縮尺・縮小する)【android】【画像表示】【エラー】

以前の投稿の続きです。↓
⇒java.lang.OutOfMemoryError: bitmap size exceeds VM budget【android】【画像表示】【エラー】 の続き


画像の表示について追記します。

以前、画像の縮小について下記の方法を書きましたが、

// 画像を1/20サイズに縮小(メモリ対策)
bmfOptions.inSampleSize = 20;


実際、表示されるすべての画像を均一に縮小するやり方はよくないということで、
画面の表示サイズを取得し、そこから必要に応じて縮小するやり方を紹介します。
以前紹介した、etBitmap(File file)を下記に変更してみました。
改善点等ございましたら、ご教授くださいませ。

    public Bitmap setBitmap(File file){
// ビットマップ作成オブジェクトの設定
BitmapFactory.Options bmfOptions = new BitmapFactory.Options();
// RGBでそれぞれ 5/6/5 ビットの色を使用(メモリ対策)
bmfOptions.inPreferredConfig = Config.RGB_565;
// GridViewの要素が横に3つ並ぶ想定で、余白も考慮し、画像の縮尺サイズを1/4に設定
// 画面のサイズに縮尺した後に、1/4にさらに縮尺
int reductionSize = Const.COLUMN_IMAGE_GRID_VIEW + 1;

if (metrics == null) {
// 現在の表示メトリクスの取得
DisplayMetrics dm = this.getResources().getDisplayMetrics();
// ビットマップのサイズを現在の表示メトリクスに合わせる(メモリ対策)
bmfOptions.inDensity = dm.densityDpi;
}
// システムメモリ上に再利用性の無いオブジェクトがある場合に勝手に解放(メモリ対策)
bmfOptions.inPurgeable = true;
// システム側で設定されているピクセル密度を無視して設定値を元に画像がスケール
bmfOptions.inScreenDensity = DisplayMetrics.DENSITY_LOW;

// 画像ファイルオブジェクトとビットマップ作成オブジェクトから、ビットマップオブジェクト作成
Bitmap bmImage = BitmapFactory.decodeFile(file.getPath(),bmfOptions);

if (metrics != null) {
float imageWidth = (float)bmImage.getWidth() / (float)metrics.widthPixels;
float imageHeight = (float)bmImage.getHeight() / (float)metrics.heightPixels;
float imageScale = Math.max(imageWidth, imageHeight);
if (imageScale > (1 / reductionSize)){
int newImageWidth = (int)((bmImage.getWidth() / imageScale) / reductionSize);
int newImageHeight = (int)((bmImage.getHeight() / imageScale) / reductionSize);
bmImage = Bitmap.createScaledBitmap(bmImage,newImageWidth,newImageHeight,false);
}else{
bmImage = Bitmap.createScaledBitmap(bmImage,bmImage.getWidth(),bmImage.getHeight(),false);
}
}
return bmImage;
}




【クラス変数宣言】
private DisplayMetrics metrics;


【onCreateメソッドで】
metrics = new DisplayMetrics();
display.getMetrics(metrics);




と、こんな感じです。
Const.COLUMN_IMAGE_GRID_VIEWは画面に表示している画像の列数です。
私のアプリでは、画像が3列表示されているので、余白も考え、
画面のサイズ/4 のサイズに縮小しています。
画面いっぱいいっぱいのサイズなら、reductionSizeを1にするとよいでしょう。

如何でしょうか?やっつけで作ったので、ちょっと不安ですが、
一応、今のところ、元気に動いているようですw

2011年9月19日月曜日

java.lang.OutOfMemoryError: bitmap size exceeds VM budget の続き【android】【画像表示】【エラー】

前回の続きです。前回↓
⇒java.lang.OutOfMemoryError: bitmap size exceeds VM budgetandroid】【画像表示】【エラー】

Activityの状態遷移(ライフサイクル)というのは、いったいどのようなものなのでしょうか。
世のサンプルソースにはonCreateメソッドのメソッドばかり実装したものが多く、
他のメソッドはどのように実装すればよいのでしょうか。

そのヒントになるのが、@IT様の下記の記事です。
⇒Androidアプリ作成の基本“Activity”とは何か?

下記のイメージ図は上記サイトの転載です。
























onCreateメソッドを実行することで、基本的に実行中までアプリの状態が遷移することから、
通常のアプリでは、onStartメソッドやonRestartメソッドは実装しなくてよさそうです。
※私自身探り探り調べながらやっていますので、何か情報持っている方は共有していただければ幸いです。


下記の場合、onPauseメソッドやonStopメソッド、onDestroyメソッドが呼ばれそうなので、
そのタイミングで、不要なデータをクリアする処理を入れるとリソースを有効的に使えそうです。

【onPauseメソッドやonStopメソッド、onDestroyメソッドが呼ばれそうなタイミング】
 ・アプリケーションを終了
 ・画面の向きを変えることでの再表示
 ・戻るボタン
 ・次の画面に遷移



何か情報が入れば追記します。

2011年9月10日土曜日

java.lang.OutOfMemoryError: bitmap size exceeds VM budget【android】【画像表示】【エラー】

下記にこの記事の内容に関連する記事があります。ご参考ください。
⇒画像を画面サイズに合わせる(縮尺・縮小する)



カメラで撮影した画像のような高画質な画像を表示すると、たびたび下記のような
メッセージが出力され、アプリケーションが強制終了してしまいます。


09-10 06:27:57.701: ERROR/dalvikvm-heap(2168): 7680000-byte external allocation too large for this process.
09-10 06:27:57.701: ERROR/GraphicsJNI(2168): VM won't let us allocate 7680000 bytes
09-10 06:27:57.712: DEBUG/skia(2168): --- decoder->decode returned false
09-10 06:27:57.732: DEBUG/AndroidRuntime(2168): Shutting down VM
09-10 06:27:57.732: WARN/dalvikvm(2168): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
09-10 06:27:57.941: ERROR/AndroidRuntime(2168): FATAL EXCEPTION: main
09-10 06:27:57.941: ERROR/AndroidRuntime(2168): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
09-10 06:27:57.941: ERROR/AndroidRuntime(2168): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
09-10 06:27:57.941: ERROR/AndroidRuntime(2168): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
09-10 06:27:57.941: ERROR/AndroidRuntime(2168): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:271)

※EclipseのDDMSのLogCatにLogは表示されています。


この場合の対処方法として、下記のようなものがあげられます。
 ・Bitmap.Configの設定をARGB_8888をやめて、RGB_565やARGB_4444を使用する(デフォルトRGB_565)
  これらは解像度の設定。
 ・inSampleSizeを設定し、画像ファイルのサイズを小さくして表示する。

  BitmapFactory.Options bmfOptions = new BitmapFactory.Options();
  bmfOptions.inSampleSize = 4;

  とすると、1辺4分の1のサイズとなり、面積は16分の1。

 ・システムメモリ上に再利用性の無いオブジェクトがある場合に勝手に解放するように設定する。
  bmfOptions.inPurgeable = true;

 ・ビットマップのサイズを現在の表示メトリクスに合わせる。
  DisplayMetrics dm = this.getResources().getDisplayMetrics();
  bmfOptions.inDensity = dm.densityDpi;

 ・使わなくなったタイミングでBitmapデータの解放を行う。
  Bitmap.recycle();
  ※Bitmap変数をクラス変数として持ち、onPause()メソッドでBitmap.recycle();
   とすればよいですが、System.gc()でよい気もします。

 ・ガーベッジコレクションを行う。
  System.gc();


ざっとこんなところですが、Bitmap.recycle()は使用したことがないので、情報いただければ幸いです。



私は今のところ下記のような対処を行い、OutOfMemoryErrorを防いでいます。
(不要な処理もありかもしれません)

①ビットマップ設定メソッドで、BitmapFactory.Optionsの設定を行う。
    /**
* ディレクトリリストに表示するBitmapを設定する。
* @param File file(設定するBitmapファイル)
* @return Bitmap bmImage(設定されたBitmap)
* @exception なし
*/

private Bitmap setBitmap(File file){

// ビットマップ作成オブジェクトの設定
BitmapFactory.Options bmfOptions = new BitmapFactory.Options();
// ARGBでそれぞれ0~127段階の色を使用(メモリ対策)
bmfOptions.inPreferredConfig = Config.ARGB_4444;
// 画像を1/20サイズに縮小(メモリ対策)
bmfOptions.inSampleSize = 20;
// システムメモリ上に再利用性の無いオブジェクトがある場合に勝手に解放(メモリ対策)
bmfOptions.inPurgeable = true;
// 現在の表示メトリクスの取得
DisplayMetrics dm = this.getResources().getDisplayMetrics();
// ビットマップのサイズを現在の表示メトリクスに合わせる(メモリ対策)
bmfOptions.inDensity = dm.densityDpi;
// 画像ファイルオブジェクトとビットマップ作成オブジェクトから、ビットマップオブジェクト作成
Bitmap bmImage = BitmapFactory.decodeFile(file.getPath(),bmfOptions);

return bmImage;
}

 

②アプリケーション終了時(戻るボタン押下時など)にSystem.gc();を行う。
 ※次回、Activityのライフサイクルも含めて説明します。



日本Androidの会がOutOfMemoryErrorについて纏めてくださっております。
http://mtnk.org/down/PDF/OutOfMemoryError.pdf