studio programacion para herramientas fundamentos desarrollo con avanzado aplicaciones android android-service

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



manual android studio avanzado (3)

Mi aplicación usa un patrón en el que inicio un servicio con Context#startService() y me Context#bindService() a él con Context#bindService() . Esto es para poder controlar la vida útil del servicio independientemente de si los clientes están actualmente vinculados a él. Sin embargo, me di cuenta recientemente de que cada vez que el sistema mata mi aplicación, pronto reinicia los servicios que se estaban ejecutando. En este punto, nunca se le dirá al servicio que se detenga, y esto está causando el agotamiento de la batería cada vez que ocurre. Aquí hay un ejemplo mínimo:

Encontré a alguien con un problema similar here , 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, inicié la aplicación, que inició el servicio y se vinculó a ella. Luego, abandoné la aplicación, lo que desvincula (pero deja el servicio en ejecución). Entonces lo hice

$ adb shell am kill com.tavianator.servicerestart

y, por supuesto, 5 segundos después, aparece la tostada "onCreate", que indica que el servicio se inició nuevamente. 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á vinculada al servicio). También funciona si nunca me enlace al servicio. Pero la combinación de inicio, vinculación y desvinculación parece que nunca deja que mi servicio muera.

Usar dumpsys antes de matar la aplicación muestra esto:

$ 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


¿Qué hay de reconsiderar su patrón ?

La bandera 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 esté enfrentando una sutil regresión en el ciclo de vida del servicio, que se introdujo en ese momento. Por lo tanto, incluso si se encuentra una solución igualmente sutil por el momento, y se comprueba mágicamente que funciona en todos los dispositivos posibles, no necesariamente funcionará en las nuevas versiones de Android.

De todos modos, ese patrón de ciclo de vida parece inusual, y esa podría ser la razón por la que te encuentras con este exótico problema.

Consideremos dos casos.

  1. Normal (?). Alguna actividad comienza el servicio. Poco tiempo después, se ata, se usa, se desata; ¿entonces qué? Detenido de alguna manera? ¿Cómo es que no hay un consumo significativo de batería en este caso?

  2. Anormal. Servicio asesinado en algún punto (al azar?). Cuando se reinicia, implementa una suposición errónea y se mantiene activa, ¿haciendo algo que drene la batería?

Todo parece bastante extraño.

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

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


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

actualizado

después de algunas investigaciones (he creado un proyecto, ejecuto el comando descrito paso a paso, etc.) encontré que el código funciona exactamente como se describe en "Cambios en el ciclo de vida del servicio"

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

asi que,
ejecuta tu aplicación,
ejecutar adb shell
en shell ejecuta el comando ps
encuentre el número PID de su aplicación
en el comando de ejecución de shell kill <app_xx_PID>
¿Dónde está tu número de PID?
repita pasos de matar para el servicio (si se está ejecutando en su propio proceso)
compruebe si el servicio se está ejecutando (no debería), reiniciado después de 5-7 segundos
actualizar
Una solución (no lo suficientemente buena, 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é se restablece el servicio cuando se mata el proceso?

De acuerdo con este document :

Cuando se inicia un servicio, tiene un ciclo de vida que es independiente del componente que lo inició y el servicio se puede ejecutar 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 su aplicación detenga sus servicios cuando esté lista para 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 el enlace para el servicio, siempre debe detener el servicio usted mismo si alguna vez recibió una llamada a onStartCommand ()

de otro documento de la mano dice:

* START_NOT_STICKY * - Si el sistema cancela el servicio después de que onStartCommand () regrese, 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 su aplicación simplemente puede reiniciar cualquier trabajo sin terminar.

Así que, después de leer este documento y algunos experimentos, creo que el sistema trata los servicios eliminados manualmente como inacabados (bloqueados: @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 se hace el trabajo?


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() ) de alguna manera impiden que el servicio se reduzca correctamente después de que está programado para reiniciarse cuando se mata el proceso.

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

  • Use startService() / stopService() solamente, no use bindService() / unbindService() en absoluto.

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

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

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

De mi prueba:

  • Usando la primera alternativa, imprimirá esta línea en el registro:

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

    pero el servicio no se reiniciará realmente después de eso.

  • La segunda alternativa hará que su servicio sea destruido después de desvincularlo (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 cambiarlo demasiado).

¡Espero que esto ayude!