[Обратите внимание, что (как указывает CommonsWare ниже) весь подход в этом ответе применяется только до 2.3.x включительно (Gingerbread) включительно. Начиная с Honeycomb Bitmap данные размещаются в куче виртуальной машины.]
Данные растрового изображения не размещаются в куче виртуальной машины. Существует ссылка на него в куче виртуальной машины (которая мала), но фактические данные выделяются в собственной куче базовой графической библиотекой Skia.
К сожалению, хотя определение BitmapFactory.decode ... () говорит, что оно возвращает ноль, если данные изображения не могут быть декодированы, реализация Skia (или, скорее, клей JNI между кодом Java и Skia) регистрирует сообщение Вы видите («VM не позволяет нам выделять байты xxxx»), а затем выдает исключение OutOfMemory с вводящим в заблуждение сообщением «Размер растрового изображения превышает бюджет VM».
Проблема не в куче виртуальных машин, а в собственной куче. Естественная куча распределяется между запущенными приложениями, поэтому объем свободного места зависит от того, какие другие приложения работают, и от их использования растрового изображения. Но, учитывая, что BitmapFactory не вернется, вам нужно определить, будет ли вызов успешным, прежде чем вы его сделаете.
Существуют подпрограммы для контроля размера собственной кучи (см. Методы getNative класса Debug). Однако я обнаружил, что getNativeHeapFreeSize () и getNativeHeapSize () не являются надежными. Поэтому в одном из моих приложений, которое динамически создает большое количество растровых изображений, я делаю следующее.
Размер собственной кучи зависит от платформы. Поэтому при запуске мы проверяем максимально допустимый размер кучи виртуальной машины, чтобы определить максимально допустимый собственный размер кучи. [Магические числа были определены при тестировании на 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 (т.е. 3 Мб).