Оки Доки. Я прошел через ад и вернулся к этой проблеме. Вот как действовать. Есть баги. В этой публикации описывается, как анализировать ошибки в реализации и обходить проблемы.
Подводя итог, вот как все должно работать. Запущенные службы будут регулярно очищаться и завершаться каждые 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 #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. "бак", и проживет недолго.Проще говоря, это баг.
Итак, цель состоит в том, чтобы ваш процесс всегда получал "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
person
Robin Davies
schedule
12.01.2013