[(CommonsWare が以下で指摘しているように) この回答のアプローチ全体は、2.3.x (Gingerbread) までしか適用されないことに注意してください。Honeycomb Bitmap データの時点で、VM ヒープに割り当てられます。]
ビットマップ データは VM ヒープに割り当てられません。それへの参照は VM ヒープ (小さい) にありますが、実際のデータは基礎となる Skia グラフィックス ライブラリによってネイティブ ヒープに割り当てられます。
残念ながら、 BitmapFactory.decode...() の定義では、画像データをデコードできなかった場合は null を返すと書かれていますが、Skia の実装 (または、Java コードと Skia の間の JNI 接着剤) は、メッセージをログに記録します。 (「VM は xxxx バイトの割り当てを許可しません」) を見てから、「ビットマップ サイズが VM の予算を超えています」という誤解を招くメッセージと共に OutOfMemory 例外をスローします。
問題は VM ヒープではなく、ネイティブ ヒープにあります。ネイティブ ヒープは実行中のアプリケーション間で共有されるため、空き領域の量は、実行中の他のアプリケーションとそのビットマップの使用状況によって異なります。ただし、BitmapFactory が返されないことを考えると、呼び出しを行う前に、呼び出しが成功するかどうかを判断する方法が必要です。
ネイティブ ヒープのサイズを監視するルーチンがあります (Debug クラスの getNative メソッドを参照してください)。しかし、getNativeHeapFreeSize() と getNativeHeapSize() は信頼できないことがわかりました。したがって、多数のビットマップを動的に作成するアプリケーションの 1 つで、次のことを行います。
ネイティブ ヒープ サイズは、プラットフォームによって異なります。そのため、起動時に最大許容 VM ヒープ サイズをチェックして、最大許容ネイティブ ヒープ サイズを決定します。[マジック ナンバーは 2.1 と 2.2 でのテストによって決定されたものであり、他の API レベルでは異なる場合があります。]
long mMaxVmHeap = Runtime.getRuntime().maxMemory()/1024;
long mMaxNativeHeap = 16*1024;
if (mMaxVmHeap == 16*1024)
mMaxNativeHeap = 16*1024;
else if (mMaxVmHeap == 24*1024)
mMaxNativeHeap = 24*1024;
else
Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap);
次に、BitmapFactory を呼び出す必要があるたびに、呼び出しの前にフォームのチェックを行います。
long sizeReqd = bitmapWidth * bitmapHeight * targetBpp / 8;
long allocNativeHeap = Debug.getNativeHeapAllocatedSize();
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap)
{
// Do not call BitmapFactory…
}
heapPad は、a) ネイティブ ヒープ サイズのレポートが「ソフト」であり、b) 他のアプリケーションのためにネイティブ ヒープにいくらかのスペースを残したいという事実を可能にするためのマジック ナンバーであることに注意してください。現在、3*1024*1024 (つまり 3M バイト) のパッドで実行しています。