Bitmapのメモリ対策
ImageViewに画像を表示する場合、表示したいサイズと大きい場合は
画像を小さくして表示するとメモリの節約にもつながります。
このサンプルでは、使用する画像のサイズを変更してBitmapを生成する手順となります。
package test.example.com.test29;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
//Buttonの宣言
import android.widget.Button;
//ImageViewの宣言
import android.widget.ImageView;
//Bitmapの宣言
import android.graphics.Bitmap;
//BitmapFactoryの宣言
import android.graphics.BitmapFactory;
//RelativeLayoutの宣言
import android.widget.RelativeLayout;
//Viewの宣言
import android.view.View;
//クリックイベント用に追加
import android.view.View.OnClickListener;
//Resourcesの宣言
import android.content.res.Resources;
//ログ出力の宣言
import android.util.Log;
public class MainActivity extends AppCompatActivity implements OnClickListener {
//setId用の管理番号
private int mNo1;//ボタン(画像表示用)
private int mNo2;//ボタン(画像消去用)
private int mNo3;//ImageView用
private Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout layout = new RelativeLayout(this);
setContentView(layout);
//座標の初期設定
int x=0;
int y=0;
int width=0;
int height=0;
x=50;
y=50;
width=200;
height=60;
Button btn1=new Button(this);
mNo1 = View.generateViewId();
btn1.setId(mNo1);
//配置を設定
RelativeLayout.LayoutParams obj1 = getLayoutObject(width, height);
obj1.leftMargin=x;
obj1.topMargin=y;
btn1.setText("画像を設定");
btn1.setOnClickListener(this);
layout.addView(btn1, obj1);
x=250;
y=50;
width=200;
height=60;
Button btn2=new Button(this);
mNo2 = View.generateViewId();
btn2.setId(mNo2);
//配置を設定
obj1 = getLayoutObject(width, height);
obj1.leftMargin=x;
obj1.topMargin=y;
btn2.setText("画像を消す");
btn2.setOnClickListener(this);
layout.addView(btn2, obj1);
//配置を設定
x=50;
y=200;
width=100;
height=100;
obj1 = getLayoutObject(width, height);
obj1.leftMargin=x;
obj1.topMargin=y;
mNo3 = View.generateViewId();
ImageView imageview=new ImageView(this);
imageview.setId(mNo3);
layout.addView(imageview, obj1);
//画像を表示します。
setImage(width,height);
//画像解放処理についてもメソッドを実装すること
}
@Override
//ボタンがクリックされたら実行されます。
public void onClick(View view) {
if(view.getId()==mNo1)
{
int width=0;
int height=0;
ImageView imageview = (ImageView)findViewById(mNo3);
width=imageview.getWidth();
height=imageview.getHeight();
setImage(width,height);//画像を表示
}
else //mNo2のケース
{
removeImage();//画像を消去
}
}
//画像を表示
//width:画面上の幅
//height:画面上の高さ
private void setImage()
{
//そのままBitmapを設定する
setImage(0, 0, 1);
}
private void setImage(int width,int height)
{
int iOpt = 0;
iOpt = 0;
setImage(width,height,iOpt);
}
//iOpt
//0:画面上のサイズにあうような大きさに再計算
//1:そのままImageViewに設置
private void setImage(int width,int height, int iOpt)
{
if(iOpt==0)
{
Resources res = getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
//trueにすることで反映させないようにする
//ここでは画像の大きさを取得するためtrueにしています
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, R.mipmap.ic_launcher, options);
int iSize = 0;
final int height2 = options.outHeight;
final int width2 = options.outWidth;
if (height2 > height || width2 > width)
{
//メモリを考慮した方法
if (width > height) {
iSize = Math.round((float)height2 / (float)height);
} else {
iSize = Math.round((float)width2 / (float)width);
}
//inSampleSizeに速度をあげるため指定する方法として
//2のべき乗にする方法があります
/*
final int height3 = height2 / 2;
final int width3 = width2 / 2;
while ((height3 / iSize) > height
&& (width3 / iSize) > width)
{
iSize *= 2;
}
*/
}
//計算したサイズを設定します
options.inSampleSize = iSize;
//Bitmapに計算した値を使用するように再設定
options.inJustDecodeBounds = false;
bitmap= BitmapFactory.decodeResource(res, R.mipmap.ic_launcher, options);
options = null;
}
else
{
bitmap=BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
}
//再度画像を設定したい場合
ImageView imageview=(ImageView)findViewById(mNo3);
imageview.setImageBitmap(bitmap);
}
//画像を消去
private void removeImage()
{
//画像を解放する
Resources res = getResources();
bitmap=BitmapFactory.decodeResource(res, R.mipmap.ic_launcher);
bitmap.recycle();
bitmap = null;
//ImageViewに設定されている画像を解除する
ImageView imageview=(ImageView)findViewById(mNo3);
imageview.setImageDrawable(null);
imageview.setImageBitmap(null);
}
//レイアウトを決定するオブジェクトを生成し渡します
public RelativeLayout.LayoutParams getLayoutObject(int width,int height)
{
return new RelativeLayout.LayoutParams(width, height);
}
}
|
画像の大きさの情報を取得することがポイントとなります。
しかし、そのまま取得しようとすると通常の画像を開いてしまうことと変わらない処理と
なってしまうため、メモリを浪費しかねません。
そのメモリ対策として「inJustDecodeBounds」の設定値を「true」にします。
この状態で指定したい画像のリソースから画像の大きさを取得します。
(例)
Resources res = getResources();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, R.mipmap.ic_launcher, options);
画面上に配置するコントロールに指定した画像を表示させるため
次のようにBitmap.Optionsのオブジェクトから画像の幅と高さを取得します。
(例)
final int height2 = options.outHeight;
final int width2 = options.outWidth;
計算したサイズを設定します
(例)
options.inSampleSize = iSize;
Bitmapに計算した値を使用するように再設定します
(例)
options.inJustDecodeBounds = false;
bitmap= BitmapFactory.decodeResource(res, R.mipmap.ic_launcher, options);
「trying to use a recycled bitmap android.graphics」というエラーが発生した場合は
使用中なのでエラーとなっているため「recycle」メソッドをコメントアウトしてください。
(例)
//bitmap.recycle();
そのときはガーベッジコレクションが回収してくれるのを待つことになると思います。
上記エラーが発生するようであれば、次のように実装しても結局は使っているのでエラーとなります。
if(bitmap.isRecycled()==false)
{
//imageview.setImageDrawable(null);
bitmap.recycle();
}
※使用中であれば「bitmap.recycle();」のステップ実行でエラーの原因となります。
また、「bitmap.recycle();」を解消しようとして次のようにしても
imageview.setImageDrawable(null);
としたのち、「bitmap.recycle();」とするとImageViewオブジェクトの画像を
nullとしてしまっているため、画像が消えてしまいます。
実行結果
|
|