Ответ 1
Есть два способа подумать о вашей фразе "размер кучи приложения":
-
Сколько кучи может использовать мое приложение до того, как будет запущена жесткая ошибка? И
-
Сколько кучи должно использовать мое приложение, учитывая ограничения версии ОС Android и аппаратного обеспечения пользовательского устройства?
Существует другой способ определения каждого из указанных выше.
Для пункта 1 выше: maxMemory()
который может быть вызван (например, в вашем основном действии onCreate()
) следующим образом:
Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));
Этот метод сообщает вам, сколько всего байтов кучи вашего приложения разрешено использовать.
Для пункта 2 выше: getMemoryClass()
который можно вызвать следующим образом:
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));
Этот метод говорит вам примерно о том, сколько мегабайт кучи, которое должно использовать ваше приложение, если оно хочет быть должным образом уважительным в отношении ограничений данного устройства и прав других приложений на запуск без повторного использования в onStop()
/onResume()
, поскольку они грубо вымываются из памяти, а ваше приложение для слонов принимает ванну в джакузи Android.
Это различие явно не документировано, насколько я знаю, но я протестировал эту гипотезу на пяти разных устройствах Android (см. ниже) и подтвердил, что это правильная интерпретация.
Для версии Android для Android maxMemory()
обычно возвращается примерно столько же мегабайт, как указано в getMemoryClass()
(т.е. примерно в миллион раз больше последнего значения).
Единственная ситуация (о которой я знаю), для которой эти два метода могут расходиться, - это внедренное устройство, на котором установлена версия Android, такая как CyanogenMod, которая позволяет пользователю вручную выбирать, насколько большой размер кучи должен быть разрешен для каждого приложение. Например, в CM эта опция появляется в разделе "Настройки CyanogenMod" / "Производительность" / "Размер кучи VM".
ПРИМЕЧАНИЕ. УКАЗЫВАЙТЕ, ЧТО УСТАНОВКА ЭТОТ ЗНАЧЕНИЕ ПРОЧИТАЕТ СООТВЕТСТВИЕ ВАШЕЙ СИСТЕМЕ, ОСОБЕННО, если вы выберете меньшее значение, чем обычно для вашего устройства.
Вот мои результаты теста, показывающие значения, возвращаемые maxMemory()
и getMemoryClass()
для четырех разных устройств, на которых запущен CyanogenMod, используя два разных (вручную заданных) значения кучи для каждого:
- G1:
- С размером кучи VM установлено значение 16 МБ:
- maxMemory: 16777216
- getMemoryClass: 16
- С размером кучи VM установлено значение 24 МБ:
- maxMemory: 25165824
- getMemoryClass: 16
- С размером кучи VM установлено значение 16 МБ:
- Мото-дроид:
- С размером кучи VM установлено значение 24 МБ:
- maxMemory: 25165824
- getMemoryClass: 24
- С размером кучи VM установлено значение 16 МБ:
- maxMemory: 16777216
- getMemoryClass: 24
- С размером кучи VM установлено значение 24 МБ:
- Nexus One:
- С размером кучи VM установлено значение 32 МБ:
- maxMemory: 33554432
- getMemoryClass: 32
- С размером кучи VM установлено значение 24 МБ:
- maxMemory: 25165824
- getMemoryClass: 32
- С размером кучи VM установлено значение 32 МБ:
- Viewsonic GTab:
- С размером кучи VM установлено значение 32:
- maxMemory: 33554432
- getMemoryClass: 32
- С размером кучи VM установлено значение 64:
- maxMemory: 67108864
- getMemoryClass: 32
- С размером кучи VM установлено значение 32:
В дополнение к вышесказанному, я протестировал на планшете Novo7 Paladin, использующем Ice Cream Sandwich. Это была, по сути, сводная версия ICS, за исключением того, что я укоренил планшет через простой процесс, который не заменяет всю ОС, и, в частности, не обеспечивает интерфейс, позволяющий вручную регулировать размер кучи.
Для этого устройства приведены следующие результаты:
- Novo7
- maxMemory: 62914560
- getMemoryClass: 60
Также (за Кишоре в комментарии ниже):
- HTC One X
- maxMemory: 67108864
- getMemoryClass: 64
И (за комментарий akauppi):
- Samsung Galaxy Core Plus
- maxMemory: (Не указано в комментарии)
- getMemoryClass: 48
- largeMemoryClass: 128
В комментарии от cmcromance:
- Galaxy S3 (желе Bean) большая куча
- maxMemory: 268435456
- getMemoryClass: 64
И (за каждый комментарий):
- LG Nexus 5 (4.4.3) нормальный
- maxMemory: 201326592
- getMemoryClass: 192
- LG Nexus 5 (4.4.3) большая куча
- maxMemory: 536870912
- getMemoryClass: 192
- Galaxy Nexus (4.3) нормальный
- maxMemory: 100663296
- getMemoryClass: 96
- Galaxy Nexus (4.3) большая куча
- maxMemory: 268435456
- getMemoryClass: 96
- Galaxy S4 Play Store Edition (4.4.2) обычный
- maxMemory: 201326592
- getMemoryClass: 192
- Galaxy S4 Play Store Edition (4.4.2) большая куча
- maxMemory: 536870912
- getMemoryClass: 192
Другие устройства
- Huawei Nexus 6P (6.0.1) нормальный
- maxMemory: 201326592
- getMemoryClass: 192
Я не тестировал эти два метода, используя специальный вариант манифеста android: largeHeap = "true", доступный с Honeycomb, но благодаря cmcromance и tencent у нас есть некоторые значения sampleHeap образца, как описано выше.
Мое ожидание (которое, по-видимому, поддерживается числами больших чисел выше), будет заключаться в том, что этот параметр будет иметь эффект, аналогичный настройке кучи вручную с помощью корневой ОС, т.е. он повысит значение maxMemory()
, оставив getMemoryClass()
. Существует еще один метод getLargeMemoryClass(), который указывает, сколько памяти допустимо для приложения, используя параметр largeHeap. Документация для getLargeMemoryClass() гласит: "большинству приложений не требуется этот объем памяти, а должен остаться с пределом getMemoryClass().
Если я правильно догадался, то использование этой опции будет иметь те же преимущества (и риски), что и использование пространства, доступного пользователю, который поднял кучу через корневую ОС (то есть, если ваше приложение использует дополнительной памяти, он, вероятно, не будет играть так же хорошо с любыми другими приложениями, которые пользователь запускает одновременно).
Обратите внимание, что класс памяти, по-видимому, не должен быть кратным 8 МБ.
Из вышеизложенного видно, что результат getMemoryClass()
неизменен для данной конфигурации устройства/ОС, а значение maxMemory() изменяется, когда куча устанавливается пользователем по-разному.
Мой собственный практический опыт заключается в том, что на G1 (который имеет класс памяти 16), если я вручную выберем 24 МБ в качестве размера кучи, я могу работать без ошибок даже тогда, когда моему использованию памяти разрешено дрейфовать до 20 МБ ( по-видимому, он может достигать 24 МБ, хотя я этого не пробовал). Но другие подобные приложения большого размера могут быть сброшены из памяти в результате моего собственного приложения. И, наоборот, мое приложение может быть сброшено из памяти, если эти другие приложения с высоким уровнем обслуживания будут выведены на передний план пользователем.
Таким образом, вы не можете переместить объем памяти, указанный maxMemory()
. И вы должны стараться оставаться в пределах, указанных getMemoryClass()
. Один из способов сделать это, если все остальное не удастся, может заключаться в ограничении функциональности для таких устройств таким образом, чтобы сохранить память.
Наконец, если вы планируете пойти по количеству мегабайт, указанных в getMemoryClass()
, мой совет будет работать долго и упорно на сохранения и восстановления вашего состояния приложения, так что пользовательский опыт практически непрерывным, если происходит цикл onStop()
/onResume()
.
В моем случае, по причине производительности, я ограничиваю свое приложение устройствами, работающими под 2.2 и выше, а это значит, что почти все устройства, на которых работает мое приложение, будут иметь memoryClass 24 или выше. Таким образом, я могу проектировать, чтобы занять до 20 Мбайт кучи и чувствовать себя уверенно, что мое приложение будет хорошо работать с другими приложениями, которые пользователь может запускать одновременно.
Но всегда будет несколько корневых пользователей, которые загрузили 2.2 или более позднюю версию Android на более старое устройство (например, G1). Когда вы сталкиваетесь с такой конфигурацией, в идеале, вам следует избавиться от использования вашей памяти, даже если maxMemory()
сообщает вам, что вы можете пойти намного выше, чем 16 МБ, что getMemoryClass()
сообщает вам, что вы должны настраивать таргетинг. И если вы не можете надежно убедиться, что ваше приложение будет жить в рамках этого бюджета, то, по крайней мере, убедитесь, что onStop()
/onResume()
работает без проблем.
getMemoryClass()
, как указано Diane Hackborn (hackbod) выше, доступна только на уровне API 5 (Android 2.0), и поэтому, как она советует, вы можете предположить, что физическое оборудование любого устройства, работающего ранее версия ОС предназначена для оптимальной поддержки приложений, занимающих кучу размером не более 16 МБ.
В отличие от этого, maxMemory()
, согласно документации, полностью возвращается к уровню API 1. maxMemory()
, в версии до 2.0, вероятно, вернет значение 16 МБ, но я вижу, что в мои (гораздо более поздние) версии CyanogenMod, пользователь может выбрать значение кучи как 12 МБ, что, по-видимому, приведет к более низкому пределу кучи, и поэтому я бы предположил, что вы продолжаете тестировать значение maxMemory()
, даже для версий OS до 2.0. Возможно, вам даже придется отказаться от запуска в маловероятном случае, когда это значение будет установлено даже ниже 16 МБ, если вам нужно указать больше, чем maxMemory()
указывает.