Управление памятью вашего приложения

В этом документе

  1. Как Android управляет памятью
    1. Общая память
    2. Выделение и освобождение памяти приложения
    3. Ограничение памяти приложения
    4. Переключение приложений
  2. Как ваше приложение должно управлять памятью
    1. Используйте службы экономно
    2. Освободите память, когда ваш пользовательский интерфейс становится невидимым
    3. Освободите память, когда памяти становится недостаточно
    4. Проверьте, сколько памяти вы должны использовать
    5. Избегайте загрязнения памяти растровыми изображениями
    6. Используйте оптимизированные контейнеры данных
    7. Будьте в курсе накладных расходов памяти
    8. Будьте осторожны с абстрактным кодом
    9. Используйте nano protobufs для сериализации данных
    10. Избегайте библиотек внедрения зависимостей
    11. Будьте осторожны при использовании внешних библиотек
    12. Оптимизация общей производительности
    13. Используйте ProGuard для удаления ненужного кода
    14. Используйте zipalign для заключительного APK
    15. Проанализируйте свое использование оперативной памяти
    16. Использование нескольких процессов

См. также

Оперативная память (RAM) является ценным ресурсом в любой среде разработки программного обеспечения, но это еще более ценным на мобильной операционной системы, где физическая память часто ограничена. Не смотря на то, что виртуальная машина Android Dalvik, выполняет рутинный сбор мусора, это не позволяет вам игнорировать, когда и где ваше приложение выделяет и освобождает память.

Для того, чтобы сборщик мусора смог вернуть память из вашего приложения, вам нужно избегать утечек памяти (обычно вызванные удержанием ссылок на объекты в глобальных членах классов) и освободить все Reference объекты в соответствующее время (как определено жизненным циклом обратных вызовов, обсуждаемым ниже). Для большинства приложений, сборщик мусора Dalvik заботится об остальном: система освобождает выделения памяти, когда соответствующие объекты выходят за рамки активных потоков вашего приложения.

Этот документ объясняет, как Android управляет процессами приложений и выделением памяти, и как вы можете активно уменьшить использование памяти при разработке для Android. Дополнительные сведения об общей практики по очистке своих ресурсов при программировании на Java, обратитесь к другим книгам или онлайн документации об управлении ссылками на ресурсы. Если вы ищете информацию о том, как анализировать память вашего приложения, как только вы собрали его, читайте Исследование использования оперативной памяти.

Как Android управляет памятью

Android не предоставляет подкачку для памяти, но используется подкачка страниц и отображение файла в память (mmapping) для управления памятью. Это означает, что любое изменение памяти, путем выделения новых объектов, или касающиеся файлов с отображением в память — прежнему находится в оперативной памяти и не может быть выгружено. Поэтому единственным способом полностью освободить память вашего приложения является освобождение ссылок на объекты, которые вы возможно храните, позволив сборщику мусора освободить память. За одним исключением: любые файлы с отображением в память без изменений, такие как код, могут быть выгружены из памяти, если система захочет использовать эту память в другом месте.

Общая память

Чтобы вместить всё необходимое в оперативной памяти, Android пытается совместно использовать страницы оперативной памяти между процессам. Он может делать это одним из следующих способов:

  • Каждый процесс приложения является ответвлением от существующего процесса, называемого Zygote. Zygote процесс запускается при загрузке системы и нагружает общие библиотеки кода и ресурсы (например, темы). Чтобы начать новый процесс приложения, система создает ответвление от процесса Zygote, загружает и запускает код приложения в новом процессе. Это позволяет большинству страниц оперативной памяти, выделенной для библиотечного кодекса и ресурсов, быть общими для всех процессов приложений.
  • Большая часть статических данных загружается с помощью отображения файла память в процесса. Это позволяет не только распределять одни и те же данные между процессами, но и позволяет выгрузить их, когда это необходимо. Пример статические данные включают в себя: Dalvik код (помещенный в предварительно скомпонованный .odex файл для прямого отображения в память), ресурсы приложений (по дизайну таблица ресурсов является структурой, которая может быть загружена, используя выравнивание zip элементов вашего APK), и традиционные элементы проекта, такой как машинный код собранный в .so файлы.
  • Во многих местах, Android разделяет ту же динамическую память между процессами, благодаря использованию явно выделенных регионов разделяемой памяти (либо с ashmem или gralloc). Например, оконные отображения хранятся в общей памяти между приложением и композитором экрана, и буферы курсора используют общую память между контент-провайдером и клиентом.

В связи с широким использованием общей памяти, определение того, сколько памяти ваше приложение использует, требуется особого внимания. Методы для правильного определения использования памяти в вашем приложении обсуждаются в статье Исследование использования оперативной памяти.

Выделение и освобождение памяти приложения

Вот некоторые факты о том, как Android выделяет и затем освобождает память вашего приложения:

  • Dalvik "куча" каждого процесса ограничена размером виртуальной памяти. Это определяет размер логической кучи, которая может расти по необходимости (но только до предела, который система определяет для каждого приложения).
  • Логический размер кучи отличается от объема физической памяти, используемой кучей. Просматривая кучу вашего приложения, Android вычисляет значение, называемое Пропорциональный размер набора (Proportional Set Size - PSS), который вычисляется как для грязных, так и чистых страниц, которые совместно используются с другими процессами, но только в соответствии с этой пропорцией, в зависимости от того, сколько приложений использует эту память. Этот (PSS) это то, что система считает отпечатком вашей физической памяти. Дополнительные сведения о PSS см. в Исследование использования оперативной памяти.
  • Dalvik куча не сжимает логический размер кучи, что означает, что Android не дефрагментирует кучу, чтобы уменьшить пространство. Android может уменьшить логический размер кучи только, когда есть неиспользуемое пространство в конце кучи. Но это не означает, что физическая память используемая в куче, не может сокращаться. После сборки мусора, Dalvik проходит по куче и находит неиспользуемые страницы, а затем возвращает эти страницы в ядро ​​с помощью madvise. Таким образом, работая в паре выделение и освобождение больших кусков должно привести к освобождению всей (или почти всей) используемой физической памяти. Однако освобождения памяти небольших выделений может быть гораздо менее эффективным, так как страница используется для небольшого выделения, которое все еще может использоваться совместно с чем-то другим, что до сих пор не освободили.

Ограничение памяти приложения

Для поддержания функциональной среды многозадачности, Android устанавливает жесткий лимит на размер кучи для каждого приложения. Точный предельный размер кучи колеблется для устройств, в зависимости от размера оперативной памяти устройства в наличии в целом. Если ваше приложение достигло максимально допустимого размера кучи, и пытается выделить больше памяти, оно получит OutOfMemoryError.

В некоторых случаях, вы можете запросить у системы точный размер кучи, имеющийся в наличии на данном устройстве — например, чтобы определить сколько данных безопасно держать в кэше. Вы можете запросить у системы вызвав метод getMemoryClass(). Он возвращает целое число, указывающее количество мегабайт доступных для кучи вашего приложения. Дальнейшее обсуждение ниже в разделе Проверьте, сколько памяти вы должны использовать.

Переключение приложений

Вместо того чтобы использовать подкачку памяти, когда пользователь переключается между приложениями, Android сохраняет процессы, которые не находятся на переднем плане ("видимые пользователю"), в кэш-памяти для недавно использованных приложений. Например, когда пользователь впервые запускает приложение, для него создается процесс, но когда пользователь выходит из приложения, этот процесс не завершается. Система сохраняет процесс в кэше, поэтому если пользователь позже возвращается в приложение, процесс повторно используется для быстрого переключения приложений.

Если ваше приложение хранится в кэше и оно использует память, в которой в настоящее время оно не нуждается, то ваше приложение, даже когда пользователь не использует его, накладывает ограничения на общую производительность системы. Поэтому, когда система работает с недостаточным количеством памяти, она может убивать процессы из LRU кэша, начиная с давно использованных процессов, но может отдать предпочтение процессу, которое использует больше всего памяти. Чтобы сохранить ваш процесс в кэше как можно дольше, следуйте советам приведенным в следующих разделах о том, когда освобождать ваши ссылки.

Более подробную информацию о том, как процессы кэшируются в то время как не работают на переднем плане и как Android решает, какие из них могут быть завершены, читайте в Процессы и потоки.

Как ваше приложение должно управлять памятью

Вы должны рассмотреть ограничения памяти на всех этапах разработки, в том числе во время проектирования приложения (прежде чем начать саму разработку). Существует много способов разработки и написания кода, который приведет к более эффективным результатам, применяя тех же методы снова и снова.

Вы должны применять следующие методы при разработке и реализации вашего приложения, чтобы сделать использование памяти более эффективным.

Используйте службы экономно

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

При запуске службы, система предпочитает всегда держать процесс для этой службы запущенным. Это делает процесс очень дорогостоящим, потому что оперативная память используемая службой не может быть использована чем-нибудь ещё или выгружена. Это уменьшает количество кэшированных процессов, которые система может сохранить в LRU кэше, что делает переключение приложений менее эффективным. Это может даже привести к пробуксовке в системе, когда памяти становится мало, и система не может поддерживать достаточно процессов для работы всех служб.

Лучший способ ограничить продолжительность жизни вашей службы является использование IntentService, который заканчивается себя, как только будет выполнена обработка намерения, которое его запустило. Для получения дополнительной информации, читайте Выполнение в фоновом сервисе .

Оставляя работающие службы, когда они не нужны это одна из самых грубых ошибок управления памятью , которое Android приложение только может сделать. Так что не будьте жадными, сохраняя сервис для вашего приложение работающим. Он не только будет увеличить риск того, что ваше приложение будет работать плохо из-за ограничений памяти, но и пользователь узнав, что приложение ведет себя неправильно, удалит его.

Освободите память, когда ваш пользовательский интерфейс становится невидимым

Когда пользователь не переходит в другое приложение и интерфейс пользователя больше не виден, вы должны освободить ресурсы, которые используются только для пользовательского интерфейса. Высвобождение ресурсов пользовательского интерфейса в это время может значительно увеличить возможности системы для кэширования процессов, который имеет непосредственное влияние на качество пользовательского опыта.

Чтобы получать уведомления, когда пользователь выходит из пользовательского интерфейса, реализуйте onTrimMemory() метод обратного вызова в ваших Activity классах. Вы должны использовать этот метод для прослушивания TRIM_MEMORY_UI_HIDDEN уровня, который указывает на то, что пользовательский интерфейс теперь скрыт, и вы должны освободить ресурсы, которые используются только вашим пользовательским интерфейсом.

Обратите внимание, что ваше приложение получает onTrimMemory() обратный вызов с TRIM_MEMORY_UI_HIDDEN только тогда, когда все компоненты пользовательского интерфейса вашего процесса приложения становятся скрытыми от пользователя. Этим он отличается от onStop() обратного вызова, который вызывается когда Activity становится скрытой, даже в случае когда пользователь перемещается на другую деятельность в вашем приложении. Таким образом, хотя вы должны реализовать onStop() для освобождения ресурсов деятельности, таких как подключение к сети или для отмены регистрации широковещательных приемников, вы, как правило, не должны освобождать свои ресурсы пользовательского интерфейса, пока вы не получите onTrimMemory(TRIM_MEMORY_UI_HIDDEN). Это гарантирует, что если пользователь переходит назад из другой деятельности в вашем приложении, ваши ресурсы пользовательского интерфейса по-прежнему доступны для быстрого возобновления деятельности.

Освободите память, когда памяти становится недостаточно

Во время любой стадии жизненного цикла вашего приложения, onTrimMemory() метод обратного вызова также сообщает вам, когда общая память устройства становится низкой. Вы должны реагировать путем дальнейшего высвобождения ресурсов на основе следующих уровней памяти, передаваемых в onTrimMemory():

  • TRIM_MEMORY_RUNNING_MODERATE

    Ваше приложение работает и не подлежит завершению, но устройство работает в условиях нехватки памяти и система активно убивать процессы в LRU кэше.

  • TRIM_MEMORY_RUNNING_LOW

    Ваше приложение работает и не подлежит завершению, но устройство работает в условиях ещё большей нехватки памяти, так что вы должны освободить неиспользуемые ресурсы для повышения производительности системы (которая непосредственно влияет на производительность вашего приложения).

  • TRIM_MEMORY_RUNNING_CRITICAL

    Ваше приложение по-прежнему работает, но система уже удалила большинство процессов в LRU кэше, так что вы должны освободить все некритичные ресурсы прямо сейчас. Если система не сможет восстановить достаточное количество оперативной памяти, она будет удалять всё в LRU кэше, и начнет завершать процессы, которые система предпочитает поддерживать, такие как этот запущенный сервис.

Кроме того, когда ваш процесс приложение в настоящее время в кэше, вы может получить один из следующих уровней в onTrimMemory():

  • TRIM_MEMORY_BACKGROUND

    Система работает в условиях нехватки памяти, и процесс практически в самом начале LRU списка. Хотя ваш процесс приложение с не высоким риском быть завершенным, система может быть уже начать завершать процессы в LRU кэше. Вам следует освободить ресурсы, которые легко восстановить, так что ваш процесс останется в списке и возобновится быстро, когда пользователь вернется к вашему приложению.

  • TRIM_MEMORY_MODERATE

    Система работает в условиях нехватки памяти, и процесс ближе к середине LRU списка. Если в системе продолжит увеличиваться нехватка памяти, есть шанс, что ваш процесс будет завершет.

  • TRIM_MEMORY_COMPLETE

    Система работает в условиях нехватки памяти, и процесс является одним из первых, который будет завершен, если система не восстановит память сейчас. Вам следует освободить все, что это не является критическим для возобновлением состояние приложения.

Поскольку onTrimMemory() метод обратного вызова был добавлен в уровне API 14, вы можете использовать onLowMemory() метод обратного вызова в качестве запасного варианта для более старых версий, который примерно эквивалентен TRIM_MEMORY_COMPLETE событию.

Примечание: Когда система начинает завершать процессы в LRU кэше, хотя это прежде всего работает снизу вверх, система обращает некоторое внимание на то, какие процессы потребляют больше памяти, и, таким образом, могут обеспечить системе больше памяти, если их завершить. Поэтому чем меньше памяти вы потребляете среди приложений в списке LRU в целом, тем больше ваши шансы остаться в списке, и иметь возможность быстро возобновить работу.

Проверьте, сколько памяти вы должны использовать

Как упоминалось ранее, каждое Android-устройство имеет различное количество доступной оперативной памяти в системе, и, таким образом, предоставляет разные ограничения кучи для каждого приложения. Вы можете вызвать getMemoryClass() чтобы получить оценку имеющейся кучи вашего приложения в мегабайтах. Если ваше приложение пытается выделить больше памяти, чем доступно здесь, оно будет получать OutOfMemoryError.

В особых ситуациях, вы можете запросить больший размер кучи, установив largeHeap атрибут в "true" в манифесте <application> тег. Если вы это сделаете, вы можете вызвать getLargeMemoryClass() для получения оценки кучи большого размера.

Однако способность просить большую кучу предназначена только для небольшого набора приложений, которые могут оправдать необходимость потреблять больше оперативной памяти (например, большое приложение для редактирования фотографий). Никогда не просите большую кучу просто потому, что вы получили ошибку о нехватке памяти, и вам нужно быстро исправить — вы должны использовать его только тогда, когда вы точно знаете, где вся ваша выделенная память, и почему так и должно оставаться. Тем не менее, даже когда вы уверены, что ваше приложение может оправдать большую кучу, вам следует избегать такого запроса. Использование дополнительной памяти будет всё больше и больше приносить ущерб общему впечатлению пользователя, поскольку сборка мусора займет больше времени и производительность системы может замедлиться при переключении задач или выполнении других распространенных операций.

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

Избегайте загрязнения памяти растровыми изображениями

При загрузке растрового изображения, держите его в памяти только в том разрешении, которое необходимо для экрана текущего устройства, масштабируйте его вниз, если исходное растровое изображение большего разрешения. Имейте в виду, что увеличение разрешения растровых изображений, приводит к соответствующему (увеличению2) необходимой памяти, потому что увеличиваются оба размера X и Y.

Примечание: На Android 2.3.x (API Уровень 10) и ниже, объекты растровых изображений всегда одного и того же размера в вашей куче приложения независимо от разрешения изображения (фактические данные пикселей хранится отдельно в аппаратной памяти). Это делает более трудной отладку памяти растровых изображений, потому что большинство инструментов анализа кучи не видит распределения аппаратной памяти. Однако, начиная с Android 3.0 (API Уровень 11), данные точек растрового изображения выделяются в Dalvik куче вашего приложения, улучшая сборку мусора и способность отладки. Так что если ваше приложение использует растровые изображения, и у вас возникли проблемы обнаружения почему ваше приложение использует некоторое количество памяти на старом устройстве, переключитесь на устройства под управлением Android 3.0 или выше, чтобы ее отладить.

Дополнительных советы по работе с растровыми изображениями можно найти в разделе Управление памятью растровых изображений.

Используйте оптимизированные контейнеры данных

Воспользуйтесь оптимизированными контейнерами для Android, такими как SparseArray, SparseBooleanArray, и LongSparseArray. В шаблонной реализации HashMap память может использоваться довольно неэффективно, потому что ей необходим отдельный объект для хранения для каждого элемента. Кроме того, SparseArray классы являются более эффективными, поскольку они исключают необходимость системы к преобразованию типов (называемых autobox ) для ключей и иногда значений (что создает еще один или два объекта для каждого элемента). И не бойтесь прибегать к обычным массивам, когда в этом есть смысл.

Будьте в курсе накладных расходов памяти

Будьте осведомлены о стоимости и накладных расходов языка и библиотек, которые вы используете, и держите эту информацию в виду при разработке вашего приложения от начала до конца. Часто вещи на поверхности, которые выглядят безобидно, на самом деле могут иметь большое количество накладных расходов. Примеры включают в себя:

  • Перечисления часто требуют более чем в два раза больше памяти, чем статические константы. Вы должны строго избегать использования перечислений в Android.
  • Каждый класс в Java (в том числе анонимные внутренние классы) использует около 500 байт кода.
  • Каждый экземпляр класса имеет 12-16 байт накладных расходов памяти.
  • Хранение единственного элемента в HashMap требует выделения дополнительного объекта записи, который берет 32 байта (см. предыдущий раздел оптимизированные контейнеры данных).

Несколько байт тут и там быстро складываются, дизайн приложения с тяжелыми классами или объектами будет страдать от этих накладных расходов. Это может поставить вас в трудное положение при анализе кучи, осознавая что ваша проблема это много мелких объектов использующих оперативную память.

Будьте осторожны с абстрактным кодом

Часто разработчики используют абстракции просто как «хорошую практику программирования", поскольку абстракции могут создать гибкость и улучшить поддержку кода. Тем не менее, абстракции вносят существенные затраты: как правило, они требуют намного больше кода, который должен быть выполнен, требуя больше времени и больше оперативной памяти для этого кода, который будет отображен в память. Так что, если ваши абстракции не предоставляют значительное преимущество, вы должны избегать их.

Используйте nano protobufs для сериализации данных

Protocol buffers это нейтральный от языка и от платформы, расширяемый механизм разработанный Google для сериализации структурированных данных — думайте о XML, но меньше, быстрее, и проще. Если вы решите использовать protobufs для ваших данных, вы всегда должны использовать nano protobufs в вашем клиентского коде. Обычные protobufs генерируют чрезвычайно подробный код, который будет вызывать много разных проблем в вашем приложении: дополнительное использование оперативной памяти, значительное увеличение размера APK, более медленное выполнения, и быстро достижение ограничения символов DEX.

Для получения дополнительной информации см. в разделе "Nano version" в protobuf readme.

Избегайте библиотек внедрения зависимостей

Использование библиотек внедрения зависимостей, таких как Guice или RoboGuice может быть привлекательными, потому что они могут упростить код, который вы пишете, и обеспечить адаптивную среду, которая полезна для тестирования и других изменений конфигурации. Тем не менее, эти библиотеки, как правило, выполняют выполняют инициализацию процесса путем сканирования кода для аннотаций, которая может требовать значительных объемов кода, который будет загружен в память, даже если вы в нем не нуждаетесь. Эти страницы отображений классов загружаются в чистую память, поэтому Android может выгрузить их, но этого не произойдет, пока страницы не останутся в памяти в течение длительного периода времени.

Будьте осторожны при использовании внешних библиотек

Код внешний библиотек часто написан не для мобильной среды, и может быть неэффективным при использовании для работы на мобильном клиенте. По крайней мере, когда вы решите использовать внешнюю библиотеку, вы должны взять на себя значительную часть портирования и обслуживания для оптимизации библиотеки для мобильных устройств. План этой работы это открыть и проанализировать библиотеку с точки зрения размера кода и размера памяти, прежде чем вообще её использовать.

Даже библиотеки якобы предназначенные для использования на Android являются потенциально опасными, потому что каждая библиотека может действовать по-другому. Например, одна библиотека может использовать nano protobufs, а другая использует micro protobufs. Теперь у вас есть две различные реализации protobuf в своем приложении. Это может и даже будет происходить с различными реализациями записи в журналы, аналитикой, библиотеками загрузка изображений, кэшированием, и всех других вещей, которые вы не ожидаете. ProGuard не спасет вас здесь, потому что они все будут иметь зависимости более низкого уровня, которые требуются функциональности для которой вы используете библиотеку. Это становится особенно проблематичным, когда вы используете Activity подкласс из библиотеки (которая, как правило, имеет широкий ряд зависимостей), когда библиотеки используют reflection (который является общим и означает, что вы должны тратить много времени вручную для настройки ProGuard, чтобы заставить его работать), и так далее.

Также будьте осторожны, чтобы не попасть в ловушку с использованием разделяемой библиотеки для одной или двух функций из десятка предоставляемых; вы не хотите тянуть в большое количество кода и накладных расходов, для функций которые вы даже не используете. В конце концов, если нет существующей реализации, которая является именно тем что вам нужно, то лучший вариант это создать собственную реализацию.

Оптимизация общей производительности

Разнообразные сведения об оптимизации общей производительности вашего приложения доступено в других документах, перечисленных в Рекомендации по производительности. Многие из этих документов включают советы по оптимизации для производительности процессора, но многие из этих советов также помогают оптимизировать использование памяти вашего приложения, например, путем сокращения количества объектов компоновки, необходимых пользовательскому интерфейсу.

Вы также должны прочитать оптимизация пользовательского интерфейса с инструментами отладки макета, и воспользоваться предложениями по оптимизации предоставляемых сервисной программой lint.

Используйте ProGuard для удаления ненужного кода

ProGuard сжимает, оптимизирует и запутывает код путем удаления неиспользуемого кода и переименовывания классов, полей и методов на семантически непонятные названия. Использование ProGuard может сделать ваш код более компактным, требуя меньшего количества страниц памяти для отображения.

Используйте zipalign для заключительного APK

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

Примечание: Google Play не принимает APK файлы, которые не выровнены.

Проанализируйте свое использование оперативной памяти

После достижения относительно стабильной сборки, начните анализировать, сколько оперативной памяти ваше приложение использует на всех этапах его жизненного цикла. Для получения информации о том, как анализировать ваше приложение, читайте Исследование использования оперативной памяти.

Использование нескольких процессов

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

Примером, когда несколько процессов может быть целесообразным, это когда создается музыкальный проигрыватель, который воспроизводит музыку из службы в течение длительного периода времени. Если всё приложение работает в одном процессе, то многие выделения памяти, выполненные для пользовательского интерфейса его деятельности должны храниться до тех пор, пока он воспроизводит музыку, даже если пользователь в настоящее время в другом приложение, и служба управляет воспроизведением. Приложение как это может быть разделено на два процесса: один для его пользовательского интерфейса, а другой для работы, которая продолжает работать в фоновом режиме в виде службы.

Вы можете задать отдельный процесс для каждого компонента приложения, объявив android:process атрибут для каждого компонента в файле манифеста. Например, можно указать, что ваша служба должна работать в процессе, отдельном от основного процесса вашего приложения, объявив новый процесс с именем "background" (но вы можете назвать процесс как угодно):

<service android:name=".PlaybackService"
         android:process=":background" />

Ваше имя процесса должно начинаться с двоеточия (':'), чтобы гарантировать, что процесс остается приватным для вашего приложения.

Прежде чем вы решите создать новый процесс, необходимо понять последствия относительно памяти. Для иллюстрации последствий каждого процесса, будем считать, что пустой процесс не делающий в основном ничего, имеет дополнительный объем памяти около 1.4MB, как показано с помощью информации дампа памяти ниже.

adb shell dumpsys meminfo com.example.android.apis:empty

** MEMINFO in pid 10172 [com.example.android.apis:empty] **
                Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
              Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
             ------  ------  ------  ------  ------  ------  ------  ------  ------
  Native Heap     0       0       0       0       0       0    1864    1800      63
  Dalvik Heap   764       0    5228     316       0       0    5584    5499      85
 Dalvik Other   619       0    3784     448       0       0
        Stack    28       0       8      28       0       0
    Other dev     4       0      12       0       0       4
     .so mmap   287       0    2840     212     972       0
    .apk mmap    54       0       0       0     136       0
    .dex mmap   250     148       0       0    3704     148
   Other mmap     8       0       8       8      20       0
      Unknown   403       0     600     380       0       0
        TOTAL  2417     148   12480    1392    4832     152    7448    7299     148

Примечание: Более подробная информация о том, как читать такие таблицы, предоставлена в разделе Исследование использования оперативной памяти. Ключевыми данными здесь являются Private Dirty и Private Clean память, которые показывают, что этот процесс, использует практически 1.4Мб невыгружаемой памяти (распределенной по куче Dalvik, аппаратные выделения, загрузки библиотек), и еще 150Кб оперативной памяти для кода, который был отображен в память для выполнения.

Этот объем памяти для пустого процесса довольно значительный, и он может быстро расти, как только вы начинаете делать работу в этом процессе. Например, здесь представлено использование памяти процесса, который создается только показать деятельность с каким-нибудь текстом внутри:

** MEMINFO in pid 10226 [com.example.android.helloactivity] **
                Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
              Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
             ------  ------  ------  ------  ------  ------  ------  ------  ------
  Native Heap     0       0       0       0       0       0    3000    2951      48
  Dalvik Heap  1074       0    4928     776       0       0    5744    5658      86
 Dalvik Other   802       0    3612     664       0       0
        Stack    28       0       8      28       0       0
       Ashmem     6       0      16       0       0       0
    Other dev   108       0      24     104       0       4
     .so mmap  2166       0    2824    1828    3756       0
    .apk mmap    48       0       0       0     632       0
    .ttf mmap     3       0       0       0      24       0
    .dex mmap   292       4       0       0    5672       4
   Other mmap    10       0       8       8      68       0
      Unknown   632       0     412     624       0       0
        TOTAL  5169       4   11832    4032   10152       8    8744    8609     134

Процесс в настоящее время почти в три раза больше, до 4 Мб, просто показав текст в пользовательском интерфейсе. Это приводит к важному выводу: если вы собираетесь разделить ваше приложение на несколько процессов, только один процесс должен нести ответственность за пользовательский интерфейс. Другие процессы должны избегать любого пользовательского интерфейса, так как это быстро увеличит объем оперативной памяти, необходимый для процесса (особенно как только вы начинаете загружать растровые изображения и другие ресурсы). Затем, может быть трудно или невозможно уменьшить использование памяти после того, как пользовательский интерфейс был отрисован.

Кроме того, при работе с более чем одним процессом, это более важно чем когда бы то ни было, даже если вы держите свой код очень максимально компактным, потому что любые ненужные накладные расходы памяти для общих реализаций теперь повторяются в каждом процессе. Например, если вы используете перечисления (хотя вы не должны использовать перечисления), вся память, необходимая для создания и инициализации этих констант дублируется в каждом процессе, и любые ваши абстракции с адаптерами и временным кодом или другие накладные расходы будут также дублироваться.

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