Класс Android Training , " Эффективное отображение растровых изображений ", предоставляет полезную информацию для понимания и обработки исключения java.lang.OutOfMemoryError: bitmap size exceeds VM budget
при загрузке растровых изображений.
Чтение размеров и типа растрового изображения
Класс BitmapFactory
предоставляет несколько методов декодирования (decodeByteArray()
, decodeFile()
, decodeResource()
и т. Д.) Для создания Bitmap
из различных источников. Выберите наиболее подходящий метод декодирования в зависимости от источника данных вашего изображения. Эти методы пытаются выделить память для созданного растрового изображения и поэтому могут легко привести к исключению OutOfMemory
. Каждый тип метода декодирования имеет дополнительные подписи, которые позволяют указывать параметры декодирования через класс BitmapFactory.Options
. Установка свойства inJustDecodeBounds
на true
во время декодирования позволяет избежать выделения памяти, возвращая null
для растрового объекта, но устанавливая outWidth
, outHeight
и outMimeType
. Этот метод позволяет считывать размеры и тип данных изображения до создания (и выделения памяти) растрового изображения.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Чтобы избежать java.lang.OutOfMemory
исключений, проверьте размеры растрового изображения перед его декодированием, если только вы не доверяете источнику, предоставляя вам данные изображения предсказуемого размера, которые удобно помещаются в доступную память.
Загрузить уменьшенную версию в память
Теперь, когда размеры изображения известны, их можно использовать, чтобы решить, следует ли загружать полное изображение в память или вместо этого следует загружать версию с субдискретизацией. Вот некоторые факторы, которые следует учитывать:
- Расчетное использование памяти при загрузке полного изображения в память.
- Объем памяти, который вы готовы выделить для загрузки этого изображения с учетом любых других требований к памяти вашего приложения.
- Размеры целевого компонента ImageView или пользовательского интерфейса, в который должно быть загружено изображение.
- Размер экрана и плотность текущего устройства.
Например, не стоит загружать изображение 1024x768 пикселей в память, если оно в конечном итоге будет отображаться в виде миниатюры 128x96 пикселей в ImageView
.
Чтобы сказать декодеру, чтобы он выполнял выборку изображения, загружая уменьшенную версию в память, установите inSampleSize
в true
в вашем BitmapFactory.Options
объекте. Например, изображение с разрешением 2048x1536, которое декодируется с inSampleSize
, равным 4, создает растровое изображение приблизительно 512x384. Загрузка этого в память использует 0,75 МБ, а не 12 МБ для полного изображения (при условии, что конфигурация растрового изображения ARGB_8888
). Вот метод для вычисления значения размера выборки, которое является степенью двойки, на основе целевой ширины и высоты:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Примечание : рассчитывается степень двух, поскольку декодер использует
окончательное значение путем округления до ближайшей степени двух, согласно
inSampleSize
документация.
Чтобы использовать этот метод, сначала выполните декодирование с inJustDecodeBounds
, установленным на true
, передайте параметры и затем снова декодируйте, используя новое значение inSampleSize
и inJustDecodeBounds
, установив false
:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Этот метод позволяет легко загружать растровое изображение произвольно большого размера в ImageView
, который отображает миниатюру 100x100 пикселей, как показано в следующем примере кода:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Вы можете выполнить аналогичный процесс для декодирования растровых изображений из других источников, заменив соответствующий метод BitmapFactory.decode*
при необходимости.