请注意,本回答中的整个方法仅适用于2.3.x(姜饼)及以下版本(如CommonsWare在下面指出)。从Honeycomb开始,位图数据分配在VM堆中。
位图数据不是在VM堆中分配的。VM堆中有对它的引用(很小),但实际数据是由底层Skia图形库在Native堆中分配的。
不幸的是,虽然BitmapFactory.decode...()的定义说如果无法解码图像数据则返回null,但Skia实现(或者更确切地说是Java代码和Skia之间的JNI粘合剂)记录了您看到的消息(“VM won't let us allocate xxxx bytes”),然后抛出一个具有误导性信息“bitmap size exceeds VM budget”的OutOfMemory异常。
问题不在VM堆中,而是在Native堆中。Native堆在运行的应用程序之间共享,因此可用空间取决于其他应用程序正在运行以及它们的位图使用情况。但是,考虑到BitmapFactory不会返回,您需要一种在进行调用之前确定调用是否成功的方法。
有一些例程可以监视Native堆的大小(请参见Debug类getNative方法)。然而,我发现getNativeHeapFreeSize()和getNativeHeapSize()不可靠。因此,在我的一个动态创建大量位图的应用程序中,我执行以下操作。
Native堆大小因平台而异。因此,在启动时,我们检查最大允许的VM堆大小以确定最大允许的Native堆大小。[这些魔术数字是通过在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) Native heap大小的报告是“软性”的;b) 我们希望在Native heap中为其他应用程序留出一些空间。目前我们使用3*1024*1024(即3MB)的pad。