【AndroidStudio】画面遷移時の画像表示で起きた「OutOfMemoryError」の回避方法

AB009

AndroidStudioでアプリを開発中に「OutOfMemoryError」が表示されました。

メモリ障害と一言でいっても、原因は様々です。今回は私が作っていたアプリで起きた「OutOfMemoryError」の原因と対処法についてまとめておきたいと思います。

 

 

エラーが発生した個所

AB011

Main1「お話」ボタンを押下すると、表示されている女の子の表情とコメントがランダムに変わる、というシンプルな機能です。

Main2画面へ遷移した後、「戻る」ボタンを押下しMain1に画面遷移する際に落ちてしまうというエラーが発生しました。女の子の表情のパーツを表示する箇所が原因でメモリに障害が出てしまったようです。

 

 

Javaのメモリと、「OutOfMemoryError」

Javaには2種類のメモリがあり、Javaオブジェクトのプログラムを格納する「Javaヒープ」、JavaVM自身のリソースを格納する「Cヒープ」があります。

そしてどちらかが不足した場合「OutOfMemoryError」が発生します。

 

 

今回の原因

今回、「OutOfMemoryError」につながってしまった原因は大きく分けて2つあります。

原因① Bitmapに変換することでファイルサイズが大きくなってしまった。

今回使用した画像は1つのファイルが1~10KB程度のpng形式の画像ファイルでした。

しかし、画像の大きさが768*1280と大きく、Bitmapクラスを使いpng画像を表示することでBitmapに変換され、かなりメモリに負担をかけてしまったようです。
しかも、3枚も重ねています。

原因② メモリの解放ができていなかった。

JavaはC言語とは違いメモリ管理はJavaが行います。

しかし今回のようにJavaでは解放されていないメモリもあるため、こちらで不要なメモリは解放するように指定する必要がありました。

 

 

BitmapFactory.Optionsを使う

原因①の回避方法:inSampleSize

1より大きい値を設定するとメモリを節約するために、小さい画像を返します。
今回は2を設定し、1/2サイズに縮小され、解像度を下げた状態で読み込ませました。

原因②の回避方法:BitmapFactory.Options

trueの場合、システムメモリ上に再利用性の無い画像を必要があれば解放します。
このオプションを付けないとMain2からMain1の遷移時に、画像が解放されずOutOfMemoryErrorが出てました。

    //Main1の「OutOfMemoryError」の原因1、2の回避方法


    public void onClick(View view) {

        faceParts();

    }

    private void SetImage(int imgResId, int partResId) {
        int part = 0;
        switch (imgResId) {
            case R.id.eyeImage:
                part = 0;
                break;
            case R.id.eyebrowImage:
                part = 1;
                break;
            case R.id.mouthImage:
                part = 2;
                break;
        }

        Resources resM = getResources();
        if (bitmaps[part] != null) {
            bitmaps[part].recycle();
        }
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2; //数字が2なので画像を1/2に縮小する
        options.inPurgeable = true; //再利用性のない画像を解放する

        bitmaps[part] = BitmapFactory.decodeResource(resM, partResId, options);

        ImageView imgView = (ImageView) findViewById(imgResId);
        imgView.setImageDrawable(null);
        imgView.setImageBitmap(null);
        imgView.setImageBitmap(bitmaps[part]);
    }


    public void faceParts() {
//ランダムで顔のパーツが変わる
        int ran = rnd.nextInt(3);
        if (ran == 0) {
            SetImage(R.id.eyeImage, R.drawable.parts01a);
            SetImage(R.id.eyebrowImage, R.drawable.parts01b);
            SetImage(R.id.mouthImage, R.drawable.parts01c);
        } else if (ran == 1) {
            SetImage(R.id.eyeImage, R.drawable.parts02a);
            SetImage(R.id.eyebrowImage, R.drawable.parts02b);
            SetImage(R.id.mouthImage, R.drawable.parts02c);
        } else if (ran == 2) {
            SetImage(R.id.eyeImage, R.drawable.parts03a);
            SetImage(R.id.eyebrowImage, R.drawable.parts03b);
            SetImage(R.id.mouthImage, R.drawable.parts03c);
        }
    }

 

 

まとめ

画像取得する際はメモリを消費して大きい画像を取得したり、メモリの解放をきちんとできていないと、「OutOfMemoryError」で落ちることがあります。

「OutOfMemoryError」を出さないためにも、読み込む前にサイズを小さくする・不要なメモリは解放するようにしてメモリの節約を意識するようにしました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です