Почему мой Android-сервис перезапускается, когда процесс был убит, хотя я использовал START_NOT_STICKY?

Мое приложение использует шаблон, в котором я запускаю службу с помощью Context # startService (), а также привязываюсь к ней с помощью Context # bindService () . Это значит, что я могу контролировать время жизни службы независимо от того, привязаны ли к ней какие-либо клиенты. Тем не менее, я недавно заметил, что всякий раз, когда мое приложение убивает система, оно вскоре перезапускает любые запущенные службы. На этом этапе службе никогда не будет сказано останавливаться, и это вызывает утечку батареи всякий раз, когда это происходит. Вот минимальный пример:

Я нашел кого-то с подобной проблемой здесь , но он никогда не был диагностирован или не разрешен.

Обслуживание:

@Override public void onCreate() { Toast.makeText(this, "onCreate", Toast.LENGTH_LONG).show(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_NOT_STICKY; } @Override public IBinder onBind(Intent intent) { return new Binder(); } 

Мероприятия:

 @Override protected void onStart() { super.onStart(); Intent service = new Intent(this, BoundService.class); startService(service); bindService(service, mServiceConnection, 0); } @Override protected void onStop() { unbindService(mServiceConnection); Toast.makeText(this, "unbindService", Toast.LENGTH_SHORT).show(); super.onStop(); } 

Чтобы проверить это, я запустил приложение, которое запустило службу и привязалось к ней. Затем я отступил от приложения, которое отпадает (но оставляет работу). Тогда я сделал

 $ adb shell am kill com.tavianator.servicerestart 

И, конечно же, через 5 секунд появляется надпись «onCreate», указывающая, что служба снова запустилась. Logcat показывает это:

 $ adb logcat | grep BoundService W/ActivityManager( 306): Scheduling restart of crashed service com.tavianator.servicerestart/.BoundService in 5000ms I/ActivityManager( 306): Start proc com.tavianator.servicerestart for service com.tavianator.servicerestart/.BoundService: pid=20900 uid=10096 gids={1028} 

Если я заменил шаблон startService () BIND_AUTO_CREATE, проблема не возникает (даже если я разбиваю приложение, пока оно все еще связано с сервисом). Он также работает, если я никогда не свяжусь с сервисом. Но сочетание начала, привязки и несвязания, похоже, никогда не позволяет моему служению умереть.

Использование dumpsys перед убийством приложения показывает это:

 $ adb shell dumpsys activity services com.tavianator.servicerestart ACTIVITY MANAGER SERVICES (dumpsys activity services) Active services: * ServiceRecord{43099410 com.tavianator.servicerestart/.BoundService} intent={cmp=com.tavianator.servicerestart/.BoundService} packageName=com.tavianator.servicerestart processName=com.tavianator.servicerestart baseDir=/data/app/com.tavianator.servicerestart-2.apk dataDir=/data/data/com.tavianator.servicerestart app=ProcessRecord{424fb5c8 20473:com.tavianator.servicerestart/u0a96} createTime=-20s825ms lastActivity=-20s825ms executingStart=-5s0ms restartTime=-20s825ms startRequested=true stopIfKilled=true callStart=true lastStartId=1 Bindings: * IntentBindRecord{42e5e7c0}: intent={cmp=com.tavianator.servicerestart/.BoundService} binder=android.os.BinderProxy@42aee778 requested=true received=true hasBound=false doRebind=false 

Solutions Collecting From Web of "Почему мой Android-сервис перезапускается, когда процесс был убит, хотя я использовал START_NOT_STICKY?"

Посмотрите на этот раздел документа : изменения жизненного цикла службы (начиная с версии 1.6)

обновленный

После некоторых исследований (создали проект, запустите описанную команду шаг за шагом и т. Д.), Я обнаружил, что код работает точно так, как описано в «Изменения жизненного цикла службы»,

Что я нашел:
adb shell am kill com.tavianator.servicerestart ничего не убивает
(Попробуйте adb shell , затем в shell am kill com.tavianator.servicerestart
Вы столкнулись с сообщением об ошибке Error: Unknown command: kill )

так,
Запустите приложение,
adb shell
Команда командной ps
Найти номер PID вашего приложения
В команде запуска оболочки kill <app_xx_PID>
Где ваш номер PID
Повторите шаги по убийству для обслуживания (если он работает в своем собственном процессе)
Проверьте, работает ли сервис (не должен), перезапускается через 5-7 секунд
Обновить
Одно решение (недостаточно хорошее, но применимое в некоторых случаях) – stopSelf() например:

 @Override public int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show(); stopSelf(); return START_NOT_STICKY; } 

Обновить
Обновленное решение

 void writeState(int state) { Editor editor = getSharedPreferences("serviceStart", MODE_MULTI_PROCESS) .edit(); editor.clear(); editor.putInt("normalStart", state); editor.commit(); } int getState() { return getApplicationContext().getSharedPreferences("serviceStart", MODE_MULTI_PROCESS).getInt("normalStart", 1); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (getState() == 0) { writeState(1); stopSelf(); } else { writeState(0); Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show(); } return START_NOT_STICKY; } 

Почему сервис становится ограниченным, когда процесс убит?

Согласно этому документу :

Когда служба запущена, у нее есть жизненный цикл, который не зависит от компонента, который его запускал, и служба может работать в фоновом режиме на неопределенный срок, даже если компонент, который начал его, уничтожен. Таким образом, служба должна останавливаться сама, когда ее работа выполняется путем вызова stopSelf () , или другой компонент может остановить ее, вызвав stopService () .
Внимание : важно, чтобы ваше приложение прекратило свои услуги, когда оно работает, чтобы не тратить ресурсы системы и не потреблять энергию батареи. При необходимости другие компоненты могут остановить службу, вызвав stopService (). Даже если вы включите привязку к службе, вы должны всегда останавливать службу самостоятельно, если она когда-либо получала вызов onStartCommand ()

Из другого документа:

* START_NOT_STICKY * – Если система убивает службу после того, как onStartCommand () возвращается, не воссоздавайте службу, если нет ожидающих намерений для доставки. Это самый безопасный вариант, чтобы не запускать службу, когда это не является необходимым, и когда ваше приложение может просто перезапустить любые незавершенные задания.

Итак, прочитав этот документ и некоторые эксперименты, я думаю, что система рассматривает вручную убитые службы как незавершенные (crashed: @see W/ActivityManager(306): Scheduling restart of crashed service ) и перезапускает его, несмотря на значение, возвращаемое onStartCommand.

StopSelf () или stopService () – нет перезапуска , почему бы и нет, если работа выполнена?

Я считаю, что проблема заключается в том, что для службы, которая была запущена при вызове startService() , внутренняя учетная bindService() для привязок (затронутая вызовами bindService() / unbindService() ) каким-то образом препятствует правильной unbindService() службы после нее Планируется перезапустить, когда процесс будет убит.

В зависимости от ваших предпочтений кажется, что есть три альтернативы, чтобы избежать этой проблемы (вы упомянули первые два уже в своем вопросе):

  • Используйте только startService() / stopService() , не используйте bindService() / unbindService() вообще.

  • Используйте bindService() / unbindService() только (с BIND_AUTO_CREATE ), не используйте startService() / stopService() вообще.

  • Если вам нужны startService() / stopService() и bindService() / unbindService() , переопределите метод onUnbind() в своей службе и вызовите stopSelf() из него:

     @Override public boolean onUnbind(Intent intent) { stopSelf(); return super.onUnbind(intent); } 

Из моего тестирования:

  • Использование первой альтернативы напечатает эту строку в журнале:

    W / ActivityManager (nnn): Планирование перезапуска аварийной службы xxxx в 5000 мс

    Но после этого служба не будет перезапущена.

  • Второй вариант приведет к тому, что ваш сервис будет уничтожен после того, как вы отвяжете (в onStop() в вашем примере).

  • Третья альтернатива в основном заставит ваш код вести себя как вторая альтернатива (без необходимости многого менять).

Надеюсь это поможет!

Как насчет пересмотра вашей картины ?

Флаг START_NOT_STICKY появился в какой-то момент во время эволюции Android (после 1.6?). Скорее всего, вы столкнулись с некоторой тонкой регрессией в цикле жизненного цикла, которая была введена в то время. Таким образом, даже если на данный момент найдено не менее тонкое решение – и магически проверено для работы на всех возможных устройствах – это не обязательно будет работать в новых версиях Android.

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

Рассмотрим два случая.

  1. Нормальный (?). Некоторые действия начинают службу. Вскоре после этого он привязывается, используется, несвязан – тогда что? Как-то остановился? Почему в этом случае нет значительного разряда батареи?

  2. Аномальные. Служба убита в некоторой (случайной?) Точке. При перезапуске он реализует некоторые неправильные предположения и остается активным, делая что-то, что истощает батарею?

Все выглядит довольно странно.

Я бы проанализировал параллельные процессы в вашем продукте. Все существенные случаи. Некоторые диаграммы и т. Д. Вероятнее всего, в результате сама потребность в таком шаблоне будет устранена.

В противном случае, похоже, что любое обходное решение будет хрупким взломом.