Использование памяти фоновых изображений Android

В проекте, над которым я работаю, используется несколько фонов «высокого разрешения» (обратите внимание на цитаты). Чтобы понять ситуацию, один из них - файл PNG размером 640x935 1,19M. Насколько мне известно, даже если Android распаковывает изображения в память как необработанные данные, это должно быть:

640 x 935 x 4 байта = 2,39 МБ

У меня проблемы с памятью в моем проекте, которые я не могу понять, и я надеюсь, что кто-то сможет пролить свет на этот вопрос. Я назову два устройства, над которыми я работаю, и некоторые результаты.

Чтобы убедиться, что это не вторичная проблема, я сделал действие, которое не загружало фон при первом создании, а затем, когда пользователь нажимает кнопку, все, что он делает, это:

findViewById(R.id.completed_block_background).setBackgroundResource(R.drawable.blockbackgroundbottom1);

Затем, используя DDMS с «Update Heap» в процессе (и сначала заставляя GC убедиться, что это не будет проблемой), я получаю следующие результаты памяти:

Nexus S: увеличение с 18 до 26 млн (разница 8 млн)

Galaxy Nexus: увеличение с 28 до 39 млн (разница в 11 млн)

Итак, как вы можете видеть, размещение этого теоретически 2,39 МБ несжатого изображения в фоновом режиме фактически увеличивает использование памяти на 8 и 11 МБ. Может кто-нибудь объяснить, почему это так и есть ли решение?

Единственное решение, которое мне удалось найти, - это использование растровых изображений для уменьшения вдвое разрешения или уменьшения формата канала (до сих пор это то, что я сделал, переключил их на 565 RGB, но это создает некоторые проблемы с полосами, которые я не могу принять).

Я бы также согласился с объяснением того, почему это происходит, на случай, если ничего нельзя поделать. Заранее спасибо.


person h4lc0n    schedule 29.10.2012    source источник
comment
Почему вы думаете, что ваш PNG-файл размером 1,19 МБ будет занимать только 2,12 МБ места в куче? Я ожидал, что он займет значительно больше.   -  person CommonsWare    schedule 29.10.2012
comment
@CommonsWare он показал расчеты: 640 x 935 x 4 байта = 2.39M, вы можете заметить его ошибку?   -  person lenik    schedule 29.10.2012
comment
@lenik: Я не видел PNG с такой паршивой степенью сжатия.   -  person CommonsWare    schedule 29.10.2012
comment
@CommonsWare, это очень детализированное изображение, я полагаю, поэтому степень сжатия такая низкая. В любом случае я преобразовал изображение в необработанное изображение BMP, чтобы убедиться, что я ничего не придумываю, и, да, это 2.39M, поэтому я все еще застрял в том, почему устройству требуется так много памяти для этого: /   -  person h4lc0n    schedule 29.10.2012
comment
Размер R.id.completed_block_background точно 640x935?   -  person CommonsWare    schedule 29.10.2012
comment
@CommonsWare Я только что создал растровое изображение, используя BitmapFactory.decodeResource, и getWidth () и getHeight () возвращают 640 и 935 соответственно.   -  person h4lc0n    schedule 29.10.2012
comment
Я не об этом спрашивал. Размер R.id.completed_block_background точно 640x935?   -  person CommonsWare    schedule 29.10.2012
comment
@CommonsWare aaah Теперь я понимаю, что вы имеете в виду ... нет, абсолютно нет, это линейный макет, охватывающий весь фон. Вот почему это делает изображение таким большим? Есть ли способ каким-либо образом сохранить исходный размер изображения?   -  person h4lc0n    schedule 29.10.2012


Ответы (1)


Вот почему это делает изображение таким большим?

Что ж, происходит следующее: setBackgroundResource(R.drawable.blockbackgroundbottom1) заставит Android сначала сделать BitmapFactory.decodeResource() вещь, с которой вы экспериментировали, но затем логика рендеринга масштабирует изображение, чтобы применить его в качестве фона. Так, например, разница в 3 МБ между Galaxy Nexus и Nexus S, вероятно, отражает разницу в размере в пикселях между представлениями LinearLayout.

Может также произойти некоторая передискретизация в зависимости от плотности экрана, в зависимости от того, где вы сохранили это изображение в дереве ресурсов.

Есть ли способ каким-либо образом сохранить исходный размер изображения?

Вне манжеты я сначала попробую поместить ее в res/drawable-nodpi/ (чтобы предотвратить автоматическую передискретизацию на основе плотности), а затем вручную получить Bitmap через версию BitmapFactory.decodeResource(), которая принимает BitmapFactory.Options, чтобы вы могли масштабировать ее по мере считывания в Если это не помогает, возможно, вам придется переместить PNG из доступных для рисования ресурсов в необработанный ресурс или активы, так как Android все равно может попытаться сохранить немасштабированную копию изображения. Я не думаю, что это произойдет, если вы сами будете напрямую использовать BitmapFactory.decodeResource(), но я не могу этого исключить.

person CommonsWare    schedule 29.10.2012
comment
Гениально ... просто гениально! Вы избавили меня от множества головных болей. Перемещение изображения в drawable-nodpi увеличивает использование памяти всего лишь на 1 Мбайт ... Спасибо! - person h4lc0n; 29.10.2012
comment
вполне возможно, самое важное быстрое исправление управления памятью, которое я когда-либо видел на SO, если вы разрабатываете приложения для Android с большими / многочисленными изображениями - person whyoz; 21.02.2014
comment
может ли кто-нибудь опубликовать код того, как установить изображение в качестве масштабированного фона с помощью этого метода? - person Mohamed Hafez; 02.10.2014
comment
Это так же просто, как разместить изображение в res / drawable-nodpi вместо res / drawable? - person Mohamed Hafez; 02.10.2014
comment
Ответил на свой вопрос, похоже, это действительно так просто! - person Mohamed Hafez; 02.10.2014
comment
Отличный материал для рисования-нодпи! Мы должны платить вам 1 доллар за каждый сохраненный Мб, так что теперь я должен вам около 10 долларов ... - person Benjamin Piette; 08.10.2014
comment
поскольку Android все еще может попытаться сохранить немасштабированную копию изображения? Почему? - person zionpi; 02.02.2016
comment
@zionpi: Android выполняет собственное кеширование ресурсов. - person CommonsWare; 02.02.2016
comment
drawable-nodpi - это решение здесь - person dtbarne; 18.04.2016
comment
Переместив изображение с drawable на drawable-nodpi, я уменьшил использование памяти с 47 МБ до 23 МБ; однако это все еще слишком. Затем я попробовал растровое изображение, и объем использования снова увеличился до 33 МБ. По-прежнему не удается достичь минимального размера 3 МБ. Почему? - person teddy; 27.10.2016
comment
@HackingBear: я предлагаю вам задать отдельный вопрос о переполнении стека, где вы предоставите минимальный воспроизводимый пример, демонстрирующий вашу проблему. - person CommonsWare; 27.10.2016