Android: BitmapFactory.decodeStream() из памяти с файлом 400 КБ с бесплатной кучей 2 МБ
Мое приложение ударяет ошибку OOM в следующей строке в источнике:
image = BitmapFactory.decodeStream(assetManager.open(imgFilename));
Перед распределением, которое заставляет приложение быть убитым с ошибкой OOM:
(...)
08-05 21:22:12.443: I/dalvikvm-heap(2319): Clamp target GC heap from 25.056MB to 24.000MB
08-05 21:22:12.443: D/dalvikvm(2319): GC_FOR_MALLOC freed <1K, 50% free 2709K/5379K, external 18296K/19336K, paused 58ms
08-05 21:22:14.513: D/dalvikvm(2319): GC_EXTERNAL_ALLOC freed <1K, 50% free 2709K/5379K, external 18296K/19336K, paused 101ms
08-05 21:22:14.903: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:22:14.903: D/dalvikvm(2319): GC_FOR_MALLOC freed 0K, 50% free 2709K/5379K, external 18312K/19336K, paused 53ms
08-05 21:22:22.843: D/ddm-heap(2319): Heap GC request
08-05 21:22:22.963: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:22:22.963: D/dalvikvm(2319): threadid=1: still suspended after undo (sc=1 dc=1)
08-05 21:22:22.963: D/dalvikvm(2319): GC_EXPLICIT freed 1K, 50% free 2710K/5379K, external 18312K/19336K, paused 116ms
DDMS сообщает о схожей картине о состоянии кучи:
Heap Size: 5.254 MB
Allocated: 2.647 MB
Free: 2.607 MB
%Used: 50.38%
#Objects 49,028
Одиночный переход по этой строке приводит к ошибке OOM:
08-05 21:26:04.783: D/dalvikvm(2319): GC_EXTERNAL_ALLOC freed <1K, 50% free 2710K/5379K, external 18312K/19336K, paused 57ms
08-05 21:26:05.023: E/dalvikvm-heap(2319): 2097152-byte external allocation too large for this process.
08-05 21:26:05.163: I/dalvikvm-heap(2319): Clamp target GC heap from 25.073MB to 24.000MB
08-05 21:26:05.163: E/GraphicsJNI(2319): VM won't let us allocate 2097152 bytes
08-05 21:26:05.163: D/dalvikvm(2319): GC_FOR_MALLOC freed 0K, 50% free 2710K/5379K, external 18312K/19336K, paused 30ms
08-05 21:26:05.283: D/skia(2319): --- decoder->decode returned false
- Сообщается, что размер файла, на который ссылается "imgFileName", равен < 400K в Windows. Итак, почему BitmapFactory.decodeStream пытается выделить 2 МБ?
- Почему существует ошибка OOM, когда, кажется, достаточно свободного места?
Это приложение предназначено для Android 2.2 и выше.
Спасибо заранее!
Ответы
Ответ 1
Android-библиотека не настолько умна для загрузки изображений, поэтому вам нужно создать обходные пути для этого.
В моих тестах Drawable.createFromStream
использует больше памяти, чем BitmapFactory.decodeStream
.
Вы можете изменить цветовую схему для уменьшения объема памяти (RGB_565), но изображение также потеряет качество:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
Ссылка: http://developer.android.com/reference/android/graphics/Bitmap.Config.html
Вы также можете загрузить масштабированное изображение, которое значительно уменьшит использование памяти, но вы должны знать свои изображения, чтобы не потерять слишком много его качества.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
Ссылка: http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html
Чтобы динамически определить inSampleSize, вы можете узнать размер изображения, чтобы принять ваше решение:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
bitmap = BitmapFactory.decodeStream(stream, null, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
options.inJustDecodeBounds = false;
// recreate the stream
// make some calculation to define inSampleSize
options.inSampleSize = ?;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
Вы можете настроить inSampleSize в соответствии с размером экрана устройства. Чтобы получить размер экрана, вы можете:
DisplayMetrics metrics = new DisplayMetrics();
((Activity) activity).getWindowManager().getDefaultDisplay().getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight =metrics.heightPixels;
Другие учебники:
- http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
- http://developer.android.com/training/displaying-bitmaps/index.html
Ответ 2
Обратитесь к этому руководству по загрузке больших растровых изображений более эффективно:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
Файл изображения 400 КБ может легко занять 5-10 МБ ОЗУ.
Ответ 3
Размер файла на диске не обязательно совпадает с размером файла в памяти. Скорее всего, файл сжимается, что не будет при декодировании. Вы должны учитывать это в своих расчетах.
Умножьте размер изображения (ширина х высота) на глубину цвета изображения, чтобы получить размер изображения в памяти.
Ответ 4
В основном вы можете решить свою проблему, пытаясь масштабировать ваш Bitmap
, и вы уменьшите потребление памяти. Для этого вы можете скопировать его метод, показанный здесь.
Кроме того, у разработчиков Android есть специальная страница, которая поможет вам лучше понять, как загружать большие растровые изображения. Взгляните на официальную документацию.
Ответ 5
В то время как вышеприведенные ответы, очевидно, правильны, лучше также установить явно свойство Imagemap/src ImageView равным null, когда они больше не используются, в основном, когда ваша деятельность уничтожается.
Любые другие сверхмощные ресурсы (большой текст, аудио, видео) и т.д. Также могут быть аннулированы.
Это гарантирует, что ресурсы будут немедленно освобождены, а не ждать сбора GC.
Ответ 6
Я изменяю размер insample на 2. Моя проблема решена. Но убедитесь, что качество изображения не уничтожается.