Ответ 1
Okey dokey. Я прошел через ад и вернулся к этой проблеме. Здесь, как действовать. Есть ошибки. В этой публикации описывается, как анализировать ошибки в реализации и решать проблемы.
Просто подведем итоги, вот как все должно работать. Запуск сервисов будет регулярно очищаться и заканчиваться каждые 30 минут или около того. Службы, которые хотят оставаться в живых дольше, чем это, должны вызвать Service.startForeground, который размещает уведомление на панели уведомлений, чтобы пользователи знали, что ваша служба постоянно работает и потенциально может всасывать время автономной работы. Только 3 процесса обслуживания могут назначать себя в качестве услуг переднего плана в любой момент времени. Если есть более трех услуг переднего плана, Android назначит самую старую службу в качестве кандидата на очистку и завершение.
К сожалению, в Android существуют ошибки в отношении приоритезации служб переднего плана, которые запускаются различными комбинациями флагов привязки сервисов. Несмотря на то, что вы правильно назначили услугу в качестве услуги переднего плана, Android может прекратить службу в любом случае, если какие-либо подключения к службам в вашем процессе когда-либо были сделаны с определенными комбинациями флагов привязки. Подробности приведены ниже.
Обратите внимание, что очень немногие службы должны быть передними службами. Как правило, вам нужно быть только передним сервисом, если у вас есть постоянно действующее или долгое подключение к Интернету, которое может быть включено и выключено или отменено пользователями. Примеры служб, которые требуют статуса переднего плана: серверы UPNP, длительная загрузка очень больших файлов, синхронизация файловых систем с помощью Wi-Fi и воспроизведение музыки.
Если вы просто периодически проводите опрос или ожидаете на системных вещательных приемниках или системных событиях, вам будет лучше не просыпаться ваш сервис по таймеру или в ответ на широковещательные приемники, а затем разрешить вашему служению умереть после завершения. Это так называемое поведение для служб. Если вы просто должны остаться в живых, тогда продолжайте читать.
Проверяя флажки на хорошо известных требованиях (например, вызывая Service.startForeground), следующее место для поиска - это флаги, которые вы используете в вызовах Context.bindService. Флаги, используемые для привязки, влияют на приоритет целевого процесса обслуживания различными неожиданными способами. В частности, использование определенных флагов привязки может привести к тому, что Android некорректно снизит вашу службу переднего плана до обычной службы. Код, используемый для назначения приоритета процесса, был сильно изменен. Примечательно, что в API 14+ есть изменения, которые могут вызывать ошибки при использовании старых флагов привязки; и в 4.2.1 есть определенные ошибки.
Друг во всем этом - это утилита sysdump, которая может использоваться для определения того, какой приоритет менеджер активности назначил вашему сервису, и выявить случаи, когда он назначил неправильный приоритет. Запустите службу и выполните следующую команду из командной строки на вашем компьютере:
Процессы активности dumpys оболочки adb > tmp.txt
Используйте блокнот (не WordPad/запись), чтобы изучить содержимое.
Сначала убедитесь, что вы успешно выполнили свою службу в состоянии переднего плана. Первый раздел файла dumpsys содержит описание свойств ActivityManager для каждого процесса. Найдите строку, подобную следующей, которая соответствует вашему приложению в первом разделе файла dumpsys:
APP UID 10068 ProcessRecord {41937d40 2205: tunein.service/u0a10068}
Убедитесь, что foregroundServices = true в следующем разделе. Не беспокойтесь о скрытых и пустых настройках; они описывают состояние деятельности в процессе и не кажутся особенно актуальными для процессов с услугами в них. Если 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. Решения о прекращении принимаются на основе полей adj. Вкратце, /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 были использованы, этому процессу была назначена установка" bak", и он не будет долго жить. Положительно, это ошибка.
Итак, цель состоит в том, чтобы ваш процесс всегда получал "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 к службе. Это приводит к тому, что приоритет процесса превышает один уровень выше, если активность находится на переднем плане, без какого-либо очевидного ущерба при приостановке или завершении операции.
Но в целом стратегия состоит в том, чтобы настроить флаги bindService, пока sysdump не укажет, что ваш процесс получил правильный приоритет.
Для моих целей, используя Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT для привязок к действию для обслуживания и Context.BIND_AUTO_CREATE | Контекст .BIND_WAIVE_PRIORITY для привязок сервисов к сервисам, кажется, делает правильные вещи. Ваш пробег может отличаться.
Мое приложение довольно сложно: две справочные службы, каждая из которых может независимо поддерживать состояния обслуживания переднего плана, а также третью, которая также может принимать состояние обслуживания переднего плана; две из этих услуг связаны друг с другом условно; третий связывается с первым, всегда. Кроме того, Activites запускаются в отдельном процессе (делает анимацию более плавной). Выполнение действий и служб в том же процессе, похоже, не имело никакого значения.
Реализация правил для процессов очистки (и исходный код, используемый для создания содержимого файлов sysdump), можно найти в файле основного андроида
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