Оки Доки. Я прошел через ад и вернулся к этой проблеме. Вот как действовать. Есть баги. В этой публикации описывается, как анализировать ошибки в реализации и обходить проблемы.
Подводя итог, вот как все должно работать. Запущенные службы будут регулярно очищаться и завершаться каждые 30 минут или около того. Службы, которые хотят работать дольше указанного срока, должны вызывать Service.startForeground, который помещает уведомление на панель уведомлений, чтобы пользователи знали, что ваша служба работает постоянно и потенциально расходует заряд батареи. Только 3 сервисных процесса могут назначать себя сервисами переднего плана в любой момент времени. Если существует более трех служб переднего плана, Android назначит самую старую службу в качестве кандидата на очистку и завершение.
К сожалению, в Android есть ошибки в отношении приоритезации служб переднего плана, которые запускаются различными комбинациями флагов привязки служб. Даже если вы правильно назначили свою службу в качестве службы переднего плана, Android в любом случае может прекратить работу вашей службы, если какие-либо подключения к службам в вашем процессе когда-либо были выполнены с определенными комбинациями флагов привязки. Подробности приведены ниже.
Обратите внимание, что очень немногие службы должны быть службами переднего плана. Как правило, вам нужно быть службой переднего плана, только если у вас есть постоянно активное или длительное подключение к Интернету какого-либо типа, которое может быть включено и выключено или отменено пользователями. Примеры служб, которым требуется статус переднего плана: серверы UPNP, длительная загрузка очень больших файлов, синхронизация файловых систем по Wi-Fi и воспроизведение музыки.
Если вы просто изредка опрашиваете или ждете приемников системных широковещательных сообщений или системных событий, вам лучше разбудить службу по таймеру или в ответ на приемники широковещательной рассылки, а затем позволить вашей службе умереть после завершения. Это стандартное поведение для сервисов. Если вы просто обязаны остаться в живых, читайте дальше.
Установив флажки для хорошо известных требований (например, при вызове Service.startForeground), следующее место, куда нужно обратить внимание, - это флаги, которые вы используете в вызовах Context.bindService. Флаги, используемые для привязки, влияют на приоритет целевого процесса службы множеством неожиданных способов. В частности, использование определенных флагов привязки может привести к тому, что Android неправильно понизит вашу службу переднего плана до обычной службы. Код, используемый для назначения приоритета процесса, был сильно переработан. Примечательно, что в API 14+ есть исправления, которые могут вызывать ошибки при использовании старых флагов привязки; и в 4.2.1 есть определенные ошибки.
Ваш друг во всем этом - служебная программа sysdump, с помощью которой можно выяснить, какой приоритет диспетчер операций назначил вашему процессу обслуживания, и выявить случаи, когда он назначил неверный приоритет. Установите и запустите свою службу, а затем введите следующую команду из командной строки на своем главном компьютере:
adb shell dumpsys activity процессы> tmp.txt
Используйте блокнот (не блокнот / запись), чтобы изучить содержимое.
Сначала убедитесь, что вам удалось успешно запустить службу в состоянии переднего плана. Первый раздел файла dumpsys содержит описание свойств ActivityManager для каждого процесса. Найдите в первом разделе файла dumpsys строку вроде следующей, которая соответствует вашему приложению:
APP UID 10068 ProcessRecord {41937d40 2205: tunein.service / u0a10068}
Убедитесь, что foregroundServices = true в следующем разделе. Не беспокойтесь о скрытых и пустых настройках; они описывают состояние Activity в процессе и не кажутся особенно актуальными для процессов, в которых есть сервисы. Если foregroundService не соответствует действительности, вам необходимо вызвать Service.startForeground, чтобы сделать его истинным.
Следующее, на что вам нужно обратить внимание, это раздел в конце файла под названием «Обработать список LRU (отсортированный по oom_adj):». Записи в этом списке позволяют определить, действительно ли Android классифицировал ваше приложение как службу переднего плана. Если ваш процесс находится в конце этого списка, это главный кандидат на полное уничтожение. Если ваш процесс находится в верхней части списка, он практически неразрушим.
Посмотрим на строку в этой таблице:
Proc
Это пример службы переднего плана, которая все сделала правильно. Ключевым полем здесь является "adj =". Это указывает на приоритет, присвоенный вашему процессу ActivityManagerService после того, как все было сказано, что сделано. Вы хотите, чтобы это было "adj = prcp" (видимая служба переднего плана); или "adj = vis" (видимый процесс с действием) или "fore" (процесс с активностью переднего плана). Если это "adj = svc" (служебный процесс), или "adj = svcb" (устаревшая служба?), Или "adj = bak" (пустой фоновый процесс), то ваш процесс является вероятным кандидатом на завершение и будет завершен. не реже, чем каждые 30 минут, даже если нет необходимости освобождать память. Остальные флаги в строке в основном представляют собой диагностическую отладочную информацию для инженеров Google. Решения о расторжении принимаются на основании полей прил. Вкратце, / FS обозначает службу переднего плана; / FA указывает процесс переднего плана с действием. / B указывает на фоновую службу. Метка в конце указывает общее правило, в соответствии с которым процессу был назначен приоритет. Обычно он должен соответствовать полю adj =; но в некоторых случаях значение adj = может быть скорректировано в сторону увеличения или уменьшения из-за флагов привязки активных привязок с другими службами или действиями.
Если вы столкнулись с ошибкой с флагами привязки, строка dumpsys будет выглядеть так:
Proc
Обратите внимание на то, что значение поля adj неправильно установлено на «adj = bak» (пустой фоновый процесс), что примерно переводится как «пожалуйста, прекратите меня сейчас, чтобы я мог положить конец этому бессмысленному существованию» в целях очистки процесса. Также обратите внимание на флаг (fg-service) в конце строки, который указывает, что «правила внешней службы использовались для определения настройки« adj ». Несмотря на то, что использовались правила fg-service, этому процессу был назначен параметр adj "бак", и долго он не проживет .. Проще говоря, это баг.
Итак, цель состоит в том, чтобы ваш процесс всегда получал "adj = prcp" (или лучше). И метод достижения этой цели - настраивать флаги привязки до тех пор, пока вам не удастся избежать ошибок в назначении приоритета.
Вот ошибки, о которых я знаю. (1) Если ЛЮБАЯ служба или действие когда-либо были привязаны к службе с использованием Context.BIND_ABOVE_CLIENT, вы рискуете, что значение параметра adj = будет понижено до "bak", даже если эта привязка больше не активна. Это особенно верно, если у вас также есть привязки между службами. Явная ошибка в исходниках 4.2.1. (2) Определенно никогда не используйте BIND_ABOVE_CLIENT для привязки сервиса к сервису. Не используйте его также для соединений типа "активность-обслуживание". Флаг, используемый для реализации поведения BIND_ABOVE_CLIENT, по-видимому, устанавливается для каждого процесса, а не для каждого соединения, поэтому он вызывает ошибки с привязками между сервисами, даже если нет активных действий для обслуживания. привязка с установленным флагом. Также, похоже, возникают проблемы с установлением приоритета, когда в процессе задействовано несколько сервисов, с привязками сервис-сервис. Кажется, помогает использование Context.BIND_WAIVE_PRIORITY (API 14) в привязках сервис-сервис. Context.BIND_IMPORTANT кажется более или менее хорошей идеей при привязке действия к службе. Это повышает приоритет вашего процесса на одну ступеньку выше, когда Activity находится на переднем плане, без какого-либо видимого вреда, когда 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.
Удачи.
PS: Вот интерпретация строк sysdump для Android 5.0. Я не работал с ними, так что делайте из них что хотите. Я считаю, что вы хотите, чтобы 4 было 'A' или 'S', и 5 было "IF" или "IB", а 1 было как можно меньше (вероятно, ниже 3, так как только 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