Окей, докей. Я прошел через ад и вернулся к этой проблеме. Вот как это сделать. Есть ошибки. Эта публикация описывает, как анализировать ошибки в реализации и обходить проблемы.
Чтобы подвести итог, вот как все должно работать. Запущенные сервисы будут регулярно очищаться и прерываться каждые 30 минут или около того. Службы, которые хотят оставаться живыми дольше, чем это, должны вызывать Service.startForeground, который помещает уведомление в панель уведомлений, чтобы пользователи знали, что ваша служба работает постоянно и потенциально может потреблять заряд батареи. Только 3 сервисных процесса могут назначить себя в качестве приоритетных сервисов в любой момент времени. Если существует более трех служб переднего плана, Android назначит самую старую службу в качестве кандидата для очистки и завершения.
К сожалению, в Android есть ошибки, связанные с установлением приоритетов приоритетных сервисов, которые запускаются различными комбинациями флагов привязки сервисов. Даже если вы правильно назвали свою службу приоритетной, Android может в любом случае прекратить ее, если когда-либо были установлены какие-либо подключения к службам в вашем процессе с определенными комбинациями флагов привязки. Подробности приведены ниже.
Обратите внимание, что очень немногие службы должны быть приоритетными. Как правило, вы должны быть приоритетным сервисом только в том случае, если у вас есть какое-либо постоянно активное или продолжительное подключение к Интернету, которое может быть включено или выключено или отменено пользователями. Примеры сервисов, которым требуется статус переднего плана: серверы UPNP, длительная загрузка очень больших файлов, синхронизация файловых систем по Wi-Fi и воспроизведение музыки.
Если вы просто время от времени проводите опросы, ожидаете приема на системных широковещательных приемниках или системных событиях, вам лучше разбудить службу по таймеру или в ответ на широковещательные приемники, а затем позволить своей службе завершиться. Это поведение, разработанное для сервисов. Если вы просто должны остаться в живых, тогда читайте дальше.
Установив флажки для общеизвестных требований (например, вызов Service.startForeground), следующее место, которое нужно посмотреть, это флаги, которые вы используете в вызовах Context.bindService. Флаги, используемые для привязки, влияют на приоритет целевого процесса обслуживания различными неожиданными способами. В частности, использование определенных флагов привязки может привести к тому, что Android неправильно переведет ваш приоритетный сервис на обычный сервис. Код, используемый для назначения приоритета процесса, довольно сильно изменился. В частности, в API 14+ есть исправления, которые могут вызывать ошибки при использовании более старых флагов привязки; и есть определенные ошибки в 4.2.1.
Ваш друг во всем этом - утилита sysdump, с помощью которой можно выяснить, какой приоритет назначил менеджер операций вашему процессу обслуживания, и выявить случаи, когда ему был назначен неправильный приоритет. Запустите службу и запустите следующую команду из командной строки на вашем хост-компьютере:
Процессы работы с adb shell в dumpsys> tmp.txt
Используйте блокнот (не wordpad / write) для просмотра содержимого.
Сначала убедитесь, что вам успешно удалось запустить службу в режиме переднего плана. Первый раздел файла dumpsys содержит описание свойств ActivityManager для каждого процесса. Найдите строку, подобную следующей, которая соответствует вашему приложению в первом разделе файла dumpsys:
APP UID 10068 ProcessRecord {41937d40 2205: tunein.service / u0a10068}
Убедитесь, что foregroundServices = true в следующем разделе. Не беспокойтесь о скрытых и пустых настройках; они описывают состояние Деятельности в процессе, и, кажется, не имеют особого отношения к процессам с услугами в них. Если foregroundService не соответствует действительности, вам нужно вызвать Service.startForeground, чтобы сделать его истинным.
Следующее, что вам нужно посмотреть, это раздел в конце файла, озаглавленный «Process LRU list (отсортирован по oom_adj):».Записи в этом списке позволяют вам определить, действительно ли Android классифицировал ваше приложение как приоритетный сервис.Если ваш процесс находится в нижней части этого списка, он является главным кандидатом на полное уничтожение.Если ваш процесс находится в верхней части списка, он практически неразрушим.
Давайте посмотрим на строку в этой таблице:
Proc #31: adj=prcp /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)
Это пример службы переднего плана, которая все делает правильно.Ключевым полем здесь является поле «adj =».Это указывает на приоритет, который ваш процесс назначил ActivityManagerService после того, как все было сказано выполненным.Вы хотите, чтобы это было "adj = prcp" (видимый сервис переднего плана);или «adj = vis» (видимый процесс с действием) или «fore» (процесс с активностью переднего плана).Если это «adj = svc» (процесс службы), или «adj = svcb» (устаревшая служба?), Или «adj = bak» (пустой фоновый процесс), то ваш процесс является вероятным кандидатом на завершение и будет прерван.не реже, чем каждые 30 минут, даже если нет необходимости восстанавливать память.Остальные флаги в строке - это в основном диагностическая отладочная информация для инженеров Google.Решения о прекращении принимаются на основании смежных полей.Вкратце, / FS указывает на приоритетный сервис;/ FA указывает на приоритетный процесс с действием./ B указывает на фоновый сервис.Метка в конце указывает общее правило, согласно которому процессу был присвоен приоритет.Обычно это должно соответствовать поле adj =;но значение adj = может быть скорректировано вверх или вниз в некоторых случаях из-за флагов привязки на активных привязках с другими службами или действиями.
Если вы столкнулись с ошибкой с флагами привязки, строка dumpsys будет выглядеть следующим образом:
Proc #31: adj=bak /FS trm= 0 2205:tunein.service/u0a10068 (fg-service)
Обратите внимание, что значение поля adj неправильно установлено на "adj =bak "(пустой фоновый процесс), что примерно переводится как" пожалуйста, прекратите меня сейчас, чтобы я мог положить конец этому бессмысленному существованию "в целях очистки процесса.Также обратите внимание на флаг (fg-service) в конце строки, который указывает, что «правила наземного обслуживания использовались для определения настройки« adj ». Несмотря на то, что использовались правила fg-service, этому процессу была назначена настройка adj».«bak», и он не будет жить долго. Проще говоря, это ошибка.
Таким образом, цель состоит в том, чтобы гарантировать, что ваш процесс всегда получает «adj = prcp» (или лучше).метод достижения этой цели - настраивать флаги привязки до тех пор, пока вам не удастся избежать ошибок в назначении приоритетов.
Вот ошибки, о которых я знаю. (1) Если ЛЮБОЙ сервис или действие когда-либо связывалось с сервисом, используяContext.BIND_ABOVE_CLIENT, вы рискуете, что параметр adj = будет понижен до «bak», даже если эта привязка больше не активна. Это особенно верно, если у вас также есть привязки между службами. Явная ошибка в 4.2.1. Источники. (2) Определенно никогда не используйте BIND_ABOVE_CLIENT для привязки между сервисами. Также не используйте его для соединений между операциями. TФлаг, используемый для реализации поведения BIND_ABOVE_CLIENT, по-видимому, устанавливается для каждого процесса, а не для каждого соединения, поэтому он вызывает ошибки с привязками сервис-сервис, даже если нет активной активности-сервисупривязка с установленным флагом.Кажется также, что существуют проблемы с установлением приоритета, когда в процессе присутствуют несколько сервисов с привязками сервис-сервис.Использование Context.BIND_WAIVE_PRIORITY (API 14) для привязок между сервисами, кажется, помогает.Context.BIND_IMPORTANT представляется более или менее хорошей идеей при связывании Activity с сервисом.Это повышает ваш приоритет процесса на одну ступень выше, когда действие находится на переднем плане, без какого-либо видимого вреда, когда действие приостановлено или завершено.
Но в целом стратегия состоит в том, чтобы настраивать флаги bindService до тех пор, пока sysdump не покажет, что ваш процесс получил правильный приоритет.
Для моих целей используется Context.BIND_AUTO_CREATE |Context.BIND_IMPORTANT для привязок активности к сервису и Context.BIND_AUTO_CREATE |Context.BIND_WAIVE_PRIORITY для привязок между сервисами, кажется, делает правильную вещь.Ваш пробег может отличаться.
Мое приложение довольно сложное: две фоновые службы, каждая из которых может независимо хранить состояния службы переднего плана, плюс третья, которая также может принимать состояние службы переднего плана;две службы связываются друг с другом условно;третий связывается с первым всегда.Кроме того, Activites запускается в отдельном процессе (делает анимацию более плавной).Запуск операций и служб в одном и том же процессе, похоже, не имеет никакого значения.
Реализация правил для процессов очистки (и исходный код, используемый для генерации содержимого файлов sysdump) можно найти восновной файл Android
frameworks\base\services\java\com\android\server\am\ActivityManagerService.java.
Bonance.
PS: Вот интерпретация строк sysdump для Android 5.0.Я с ними не работал, так что делай из них что хочешь.Я полагаю, что вы хотите, чтобы 4 было «A» или «S», а 5 - «IF» или «IB», а 1 - как можно ниже (вероятно, ниже 3, поскольку активны только три три процесса обслуживания переднего плана).в конфигурации по умолчанию).
Example:
Proc # : prcp F/S/IF trm: 0 31719: neirotech.cerebrum.attention:blePrcs/u0a77 (fg-service)
Format:
Proc # {1}: {2} {3}/{4}/{5} trm: {6} {7}: {8}/{9} ({10}
1: Order in list: lower is less likely to get trimmed.
2: Not sure.
3:
B: Process.THREAD_GROUP_BG_NONINTERACTIVE
F: Process.THREAD_GROUP_DEFAULT
4:
A: Foreground Activity
S: Foreground Service
' ': Other.
5:
-1: procState = "N ";
ActivityManager.PROCESS_STATE_PERSISTENT: procState = "P ";
ActivityManager.PROCESS_STATE_PERSISTENT_UI:procState = "PU";
ActivityManager.PROCESS_STATE_TOP: procState = "T ";
ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND: procState = "IF";
ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND: procState = "IB";
ActivityManager.PROCESS_STATE_BACKUP:procState = "BU";
ActivityManager.PROCESS_STATE_HEAVY_WEIGHT: procState = "HW";
ActivityManager.PROCESS_STATE_SERVICE: procState = "S ";
ActivityManager.PROCESS_STATE_RECEIVER: procState = "R ";
ActivityManager.PROCESS_STATE_HOME: procState = "HO";
ActivityManager.PROCESS_STATE_LAST_ACTIVITY: procState = "LA";
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: procState = "CA";
ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: procState = "Ca";
ActivityManager.PROCESS_STATE_CACHED_EMPTY: procState = "CE";
{6}: trimMemoryLevel
{8} Process ID.
{9} process name
{10} appUid