¿Por qué se reinicia mi servicio de Android cuando se cancela el proceso, aunque utilicé START_NOT_STICKY?

Mi aplicación utiliza un patrón en el que inicio un servicio con Contexto # startService () así como enlazar con Context # bindService () . Esto es para que pueda controlar la vida útil del servicio independientemente de si algún cliente está actualmente vinculado a él. Sin embargo, me di cuenta recientemente que cada vez que mi aplicación es asesinada por el sistema, pronto reinicia los servicios que se estaban ejecutando. En este punto el servicio nunca se le dirá que se detenga, y esto está causando el drenaje de la batería cuando sucede. Aquí hay un ejemplo mínimo:

Encontré a alguien con un problema similar aquí , pero nunca fue diagnosticado o resuelto.

Servicio:

@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(); } 

Actividad:

 @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(); } 

Para probarlo, lancé la aplicación, que inició el servicio y se unió a ella. Entonces me retiré de la aplicación, que desvincula (pero deja el servicio en marcha). Entonces lo hice

 $ adb shell am kill com.tavianator.servicerestart 

Y con seguridad, 5 segundos más tarde, el "onCreate" brindis aparece, lo que indica que el servicio comenzó de nuevo. Logcat muestra esto:

 $ 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} 

Si reemplazo el patrón startService () con BIND_AUTO_CREATE, el problema no se produce (incluso si bloqueo la aplicación mientras todavía está enlazada al servicio). También funciona si nunca me enlazo con el servicio. Pero la combinación de empezar, enlazar y desatar parece no dejar que mi servicio muera.

El uso de dumpsys antes de matar la aplicación muestra lo siguiente:

 $ 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 

Consulte la sección de este documento : Cambios en el ciclo de vida del servicio (desde 1.6)

actualizado

Después de algunas investigaciones (han creado el proyecto, ejecutar el comando descrito paso a paso, etc) he encontrado que el código está funcionando exactamente como se describe en "Service lifecycle changes"

lo que encontré:
adb shell am kill com.tavianator.servicerestart no mata nada
( adb shell , entonces en shell am kill com.tavianator.servicerestart
Se enfrentará con el mensaje de error Error: Unknown command: kill )

asi que,
Ejecutar su aplicación,
Ejecutar adb shell
En shell ejecutar ps comando
Encuentra el número PID de tu aplicación
En el comando de ejecución de shell kill <app_xx_PID>
Dónde está tu número de identificación personal
Repetir los pasos de matar para el servicio (si se está ejecutando en su propio proceso)
Verifique si el servicio está en ejecución (no debería), reiniciado después de 5-7 segundos
actualizar
Una solución (no lo suficientemente bueno, pero utilizable en algunos casos) es stopSelf() por ejemplo:

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

actualizar
Solución actualizada

 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; } 

¿Por qué el servicio se restringe cuando se cancela el proceso?

Según este documento :

Cuando se inicia un servicio, tiene un ciclo de vida independiente del componente que lo inició y el servicio puede ejecutarse en segundo plano indefinidamente, incluso si el componente que lo inició se destruye. Como tal, el servicio debe detenerse cuando su trabajo se realiza llamando a stopSelf () , u otro componente puede detenerlo llamando a stopService () .
Precaución : Es importante que la aplicación detenga sus servicios cuando se hace funcionar, para evitar desperdiciar recursos del sistema y consumir energía de la batería. Si es necesario, otros componentes pueden detener el servicio llamando a stopService (). Incluso si habilita la vinculación para el servicio, siempre debe detener el servicio usted mismo si alguna vez recibió una llamada a onStartCommand ()

De otra mano el documento dice:

* START_NOT_STICKY * – Si el sistema mata el servicio después de devuelto onStartCommand (), no vuelva a crear el servicio, a menos que haya intenciones pendientes de entregar. Esta es la opción más segura para evitar ejecutar su servicio cuando no es necesario y cuando la aplicación simplemente puede reiniciar cualquier trabajo sin terminar.

Por lo tanto, después de leer este documento y algunos experimentos creo que el sistema trata a los servicios manualmente muertos como inacabados (se bloqueó: @see W/ActivityManager(306): Scheduling restart of crashed service bloqueado) y lo reinicia a pesar del valor devuelto por onStartCommand.

StopSelf () o stopService () – no se reinicia , ¿por qué no si el trabajo hecho?

Creo que el problema aquí es que para un servicio que se inició llamando a startService() , la contabilidad interna de sus enlaces (afectados por las llamadas a bindService() / unbindService() ) están de alguna manera impidiendo que el servicio se derribe correctamente después de que Está programado para reiniciarse cuando el proceso se cancela.

Dependiendo de su preferencia, parece que hay tres alternativas para evitar este problema (usted mencionó los dos primeros ya en su pregunta):

  • Utilice sólo startService() / stopService() , no utilice bindService() / unbindService() .

  • Utilice bindService() / unbindService() solamente (con BIND_AUTO_CREATE ), no utilice startService() / stopService() en absoluto.

  • Si necesita startService() / stopService() y bindService() / unbindService() , anule el método onUnbind() en su servicio y llame a stopSelf()

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

De mis pruebas:

  • Utilizando la primera alternativa se imprimirá esta línea en el registro:

    W / ActivityManager (nnn): Programación de reinicio del servicio bloqueado xxxx en 5000ms

    Pero el servicio no será realmente reiniciado después de eso.

  • La segunda alternativa hará que su servicio sea destruido después de desvincularse (en la actividad onStop() en su ejemplo).

  • La tercera alternativa básicamente hará que su código se comporte como la segunda alternativa (sin necesidad de cambiarla por mucho).

¡Espero que esto ayude!

¿Qué tal reconsiderar su patrón ?

El indicador START_NOT_STICKY apareció en algún momento durante la evolución de Android (después de 1.6?). Lo más probable es que enfrente una sutil regresión en el ciclo de vida del servicio, que se introdujo en ese momento. Por lo tanto, incluso si una solución igualmente sutil se encuentra por ahora – y verificado mágicamente para trabajar en todos los dispositivos posibles – no necesariamente funcionará bajo nuevas versiones de Android.

De todos modos, ese patrón de ciclo de vida parece inusual, y que podría ser la razón de su ejecución en este problema exótico.

Consideremos dos casos.

  1. Normal (?). Alguna actividad inicia el servicio. Pronto después de eso consigue atado, usado, unbound – entonces qué? Detenido de alguna manera? ¿Cómo es que no hay un drenaje significativo de la batería en este caso?

  2. Anormal. Servicio muerto en algún punto (al azar?). Cuando se reinicia, se implementa alguna suposición equivocada y se mantiene activo, haciendo algo que drena la batería?

Todo parece bastante extraño.

Yo analizaría los procesos concurrentes en su producto. Todos los casos significativos. Algunos diagramas, etc. Lo más probable es que, como resultado, se eliminaría la necesidad de tal patrón.

De lo contrario, parece que cualquier solución será un hack frágil.

  • BroadcastReceiver intentando devolver el resultado durante una transmisión no ordenada - PACKAGE_ADDED en Android
  • Captura de notificaciones de otras aplicaciones
  • ¿Por qué el servicio Android falla con NullPointerException?
  • Java.lang.IllegalAccessError ref de clase en la clase preverified resuelto a la implementación inesperada
  • BroadcastReceiver cuando se cambia el estado de la red wifi o 3g
  • El servicio se está matando mientras se mantiene el bloqueo de la estela y después de llamar a startForeground
  • ¿Cómo ejecutar un servicio de 9 AM a 4 PM todos los días?
  • Preferencias compartidas entre dos procesos de la misma aplicación
  • START_STICKY y START_NOT_STICKY
  • ¿Se puede utilizar un LoaderManager de un servicio?
  • ¿Cómo mantener un servicio ejecutándose en segundo plano incluso después de que el usuario abandone la aplicación?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.