Android:ビットマップrecycle()はどのように機能しますか?


88

のようなビットマップオブジェクトに画像をロードしたとしましょう

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

次のような別のビットマップをロードするとどうなりますか

myBitmap = BitmapFactory.decodeFile(myFile2);

最初のmyBitmapはどうなりますか?それはガベージコレクトを取得しますか、または別のビットマップをロードする前に手動でガベージコレクトする必要がありますか。 myBitmap.recycle()

また、大きな画像を読み込んで、途中でリサイクルしながら次々に表示するより良い方法はありますか?

回答:


77

最初のビットマップは、2番目のビットマップをデコードするときにガベージコレクションされません。ガベージコレクターは後で決定するたびにそれを行います。できるだけ早くメモリを解放したい場合はrecycle()、2番目のビットマップをデコードする直前に呼び出す必要があります。

本当に大きな画像をロードしたい場合は、再サンプリングする必要があります。次に例を示します画像をビットマップオブジェクトに読み込んでいるときの奇妙なメモリ不足の問題


22

問題はこれだと思います。Honeycomb以前のAndroidバージョンでは、実際の生のビットマップデータはVMメモリではなくネイティブメモリに保存されます。このネイティブメモリ、対応するJava BitmapオブジェクトがGC されるときに解放されます。

ただし、ネイティブメモリが不足すると、dalvik GCはトリガーされないため、アプリがJavaメモリをほとんど使用しない可能性があるため、dalvik GCが呼び出されることはありませんが、ビットマップには大量のネイティブメモリが使用されます最終的にOOMエラーが発生します。

少なくともそれは私の推測です。ありがたいことに、Honeycomb以降では、すべてのビットマップデータがVMに保存されるためrecycle()、まったく使用する必要はありません。しかし、数百万人の2.3ユーザー(断片化が拳を揺さぶる)の場合は、recycle()可能な限り使用する必要があります(大規模な手間)。または、代わりにGCを呼び出すことができる場合もあります。


21

次の画像をロードする前に、myBitmap.recycle()を呼び出す必要があります。

myFileのソースに応じて(たとえば、元のサイズを制御できないものである場合)、単に任意の数値をリサンプリングするだけでなく、イメージをロードするときは、イメージを表示サイズにスケーリングする必要があります。

if (myBitmap != null) {
    myBitmap.recycle();
    myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
    original.recycle();
original = null;

アクティビティの開始時に初期化したstaticにdisplayWidthとdisplayHeightをキャッシュします。

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();

3
recycle()を呼び出す必要はありません。メモリをすぐに解放したい場合は、それだけで十分です。
Karu、2012年

13
受け入れられた答えは、「メモリをできるだけ早く解放したい場合は、recycle()を呼び出す必要があります」です。あなたの答えは「myBitmap.recycle()を呼び出す必要がある」と述べています。「すべき」と「必要」には違いがあり、この場合後者は正しくありません。
Karu

1
コンテキストは重要です。問題は、「大きな画像を読み込んで、次から次へとリサイクルするためのより良い方法もあるか」ということでした。
djunod

3
Android 4.1以降では、createScaledBitmapが元のインスタンスと同じインスタンスを返す場合があるため、上記の例は機能しない可能性があります。つまり、オリジナルをリサイクルする前に、オリジナルの!= myBitmapを確認する必要があります。
Jeremyfa 2013年

1
@Jeremyfa元の画像と同じ幅と高さを指定した場合にのみ、元の画像を返します。その場合、スケーリングは重要ではないため、一部のプロセスをスキップして元の画像を返すことで節約することもできます。しかし、それは何も「壊す」べきではありません...
Jabari

11

ビットマップがメモリに読み込まれると、実際には2つの部分のデータによって作成されました。最初の部分にはビットマップに関する情報が含まれ、別の部分にはビットマップのピクセルに関する情報が含まれます(バイト配列で構成されています)。最初の部分はJava使用メモリに存在し、2番目の部分はC ++使用メモリに存在します。お互いのメモリを直接使用できます。Bitmap.recycle()は、C ++のメモリを解放するために使用されます。それだけを行うと、GCはjavaの一部を収集し、Cのメモリが常に使用されます。


+1は、メモリが即時GCに利用できない理由を説明する興味深いが非常に良い方法です-素晴らしい方法です。
Richard Le Mesurier 2013

8

ティムムは正しかった。

によると:http : //developer.android.com/training/displaying-bitmaps/cache-bitmap.html

さらに、Android 3.0(APIレベル11)より前のバージョンでは、ビットマップのバッキングデータはネイティブメモリに格納されていたため、予測可能な方法で解放されず、アプリケーションがメモリ制限を一時的に超えてクラッシュする可能性がありました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.