Передний план убивает Android - PullRequest
75 голосов
/ 11 июля 2011

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

У меня почти такая же проблема, как и в этом вопросе: Служба уничтожается при удерживании блокировки пробуждения и после вызова startForeground , включая устройство (Asus Transformer), время до остановки службы (30-45 минут), использование wake-блокировки, startForeground () и тот факт, что проблема не возникает, если приложениеоткрыт, когда экран гаснет.

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

Согласно ответу в связанном вопросе startForeground () «уменьшает вероятность того, что служба будет убита, но не предотвращает ее».Я понимаю, что это так, однако я видел много примеров других приложений, у которых нет этой проблемы (например, Tasker).

Полезность моего приложения будет значительно снижена без возможностислужба запускается до тех пор, пока не остановится пользователем.Есть ли способ избежать этого ???

Я вижу это в своем logcat всякий раз, когда служба останавливается:

ActivityManager: No longer want com.howettl.textab (pid 32321): hidden #16
WindowManager: WIN DEATH: Window{40e2d968 com.howettl.textab/com.howettl.textab.TexTab paused=false
ActivityManager: Scheduling restart of crashed service com.howettl.textab/.TexTabService in 5000ms

РЕДАКТИРОВАТЬ: я должен также отметить, что это, кажется, не происходитна другом устройстве, к которому я подключен: HTC Legend под управлением Cyanogen

РЕДАКТИРОВАТЬ: Здесь вывод adb shell dumpsys activity services:

* ServiceRecord{40f632e8 com.howettl.textab/.TexTabService}

intent={cmp=com.howettl.textab/.TexTabService}

packageName=com.howettl.textab

processName=com.howettl.textab

baseDir=/data/app/com.howettl.textab-1.apk

resDir=/data/app/com.howettl.textab-1.apk

dataDir=/data/data/com.howettl.textab

app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104}

isForeground=true foregroundId=2 foregroundNoti=Notification(contentView=com.howettl.textab/0x1090087 vibrate=null,sound=null,defaults=0x0,flags=0x6a)

createTime=-25m42s123ms lastActivity=-25m42s27ms

 executingStart=-25m42s27ms restartTime=-25m42s124ms

startRequested=true stopIfKilled=false callStart=true lastStartId=1

Bindings:

* IntentBindRecord{40a02618}:

  intent={cmp=com.howettl.textab/.TexTabService}

  binder=android.os.BinderProxy@40a9ff70

  requested=true received=true hasBound=true doRebind=false

  * Client AppBindRecord{40a3b780 ProcessRecord{40bb0098 2995:com.howettl.textab/10104}}

    Per-process Connections:

      ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}

All Connections:

  ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}

И вывод adb shell dumpsys activity:

* TaskRecord{40f5c050 #23 A com.howettl.textab}

numActivities=1 rootWasReset=false

affinity=com.howettl.textab

intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab}

realActivity=com.howettl.textab/.TexTab

lastActiveTime=4877757 (inactive for 702s)

* Hist #1: ActivityRecord{40a776c8 com.howettl.textab/.TexTab}

    packageName=com.howettl.textab processName=com.howettl.textab

    launchedFromUid=2000 app=ProcessRecord{40bb0098 2995:com.howettl.textab/10104}

    Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.howettl.textab/.TexTab }

    frontOfTask=true task=TaskRecord{40f5c050 #23 A com.howettl.textab}

    taskAffinity=com.howettl.textab

    realActivity=com.howettl.textab/.TexTab

    base=/data/app/com.howettl.textab-1.apk/data/app/com.howettl.textab-1.apk data=/data/data/com.howettl.textab

    labelRes=0x7f060000 icon=0x7f020000 theme=0x0

    stateNotNeeded=false componentSpecified=true isHomeActivity=false

    configuration={ scale=1.0 imsi=0/0 loc=en_CA touch=3 keys=2/1/1 nav=1/2 orien=L layout=0x10000014 uiMode=0x11 seq=6}

    launchFailed=false haveState=true icicle=Bundle[mParcelledData.dataSize=1644]

    state=STOPPED stopped=true delayedResume=false finishing=false

    keysPaused=false inHistory=true visible=false sleeping=true idle=true

    fullscreen=true noDisplay=false immersive=false launchMode=2

    frozenBeforeDestroy=false thumbnailNeeded=false

    connections=[ConnectionRecord{40a76920 com.howettl.textab/.TexTabService:@40b998b8}]

...

Proc #15: adj=prcp /F 40e75070 959:android.process.acore/10006 (provider)

          com.android.providers.contacts/.ContactsProvider2<=Proc{40bb0098 2995:com.howettl.textab/10104}

Proc #16: adj=bak+2/F 40bb0098 2995:com.howettl.textab/10104 (foreground-service)

Похоже, что служба работает на переднем плане.

Ответы [ 2 ]

203 голосов
/ 12 января 2013

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

Чтобы подвести итог, вот как все должно работать. Запущенные сервисы будут регулярно очищаться и прерываться каждые 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 
6 голосов
/ 11 июля 2011

Если он говорит «больше не хочу ...», то у этого процесса нет активной службы, которая в данный момент находится в состоянии startForeground ().Убедитесь, что ваш вызов на самом деле успешен - что вы видите опубликованное уведомление, в этот момент в журнале нет сообщений, жалующихся на что-либо и т. Д. Также используйте «службы активности adb shell dumpsys», чтобы просмотретьсостояние вашего сервиса и убедитесь, что он действительно помечен как передний план.Также, если это правильно передний план, то в выводе "adb shell dumpsys активность" вы увидите в разделе, показывающем OOM adj процессов, которые ваш процесс в настоящее время находится на переднем уровне благодаря этой службе.

...