53 votes

Pourquoi mon service Android est-il redémarré lorsque le processus est arrêté alors que j'ai utilisé START_NOT_STICKY?

Mon application utilise un modèle où j'ai démarrer un service avec un Contexte#startService() ainsi que de la lier avec le Contexte#bindService(). C'est ainsi que je peux contrôler la durée de vie du service, indépendamment de savoir si tous les clients sont actuellement liés à elle. Cependant, j'ai remarqué récemment que chaque fois que mon application est tué par le système, il redémarre tous les services qui ont été en cours d'exécution. À ce stade, le service ne sera jamais dit d'arrêter, et c'est à l'origine de l'épuisement de la batterie à chaque fois que ça arrive. Voici un exemple minimal:

J'ai trouvé quelqu'un avec un problème similaire ici, mais ce n'était pas un diagnostic ou résolu.

Service:

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

Activité:

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

Pour le tester, j'ai lancé l'application, qui a commencé le service et lié à elle. Puis je l'ai retiré de l'app, qui se délie (mais quitte le service en cours d'exécution). Puis je l'ai fait

$ adb shell am kill com.tavianator.servicerestart

et bien sûr, 5 secondes plus tard, le "onCreate" toast s'affiche, indiquant que le service a commencé à nouveau. Logcat montre ceci:

$ 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 je remplace la startService() modèle avec BIND_AUTO_CREATE, le problème ne se produit pas (même si je le plantage de l'application pendant qu'il est encore lié pour le service). Il fonctionne aussi si je n'ai jamais lier au service. Mais la combinaison de démarrer, de se lier et de délier semble ne jamais laisser mon service de mourir.

À l'aide de dumpsys avant de tuer l'application affiche ceci:

$ 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

30voto

ra. Points 1160

Oeil à ce document de l'article: Service changements de cycle de vie (depuis la 1.6)

mise à jour

après quelques recherches (qui ont créé le projet, exécutez décrit commande, étape par étape, etc) j'ai trouvé que le code est exactement comme décrit dans "Service changements de cycle de vie"

ce que j'ai trouvé:
adb shell am kill com.tavianator.servicerestart tue rien
(essayez adb shell , puis dans la coquille am kill com.tavianator.servicerestart
Vous serez confronté à d'erreur message d' Error: Unknown command: kill)

donc,
exécutez votre application,
exécutez adb shell
shell, exécutez pscommande
trouver numéro de PID de Votre application
shell, exécutez la commande kill <app_xx_PID>
où est Votre numéro de PID
répétez tuer les étapes pour le service (si elle est en cours d'exécution dans son propre processus)
vérifiez si le service est en cours d'exécution (ne devrait pas), redémarré après 5 à 7 sec
mise à jour
une solution (pas assez bon, mais utilisable dans certains cas) est - stopSelf() par exemple:

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


mise à jour
mise à jour de la solution

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

Pourquoi le service obtenir restrted lorsque le processus est tué?

Selon ce document:

Lorsqu'un service est démarré, il a un cycle de vie qui est indépendant de le composant que tout a commencé et le service peut s'exécuter dans l' fond indéfiniment, même si le composant qui a débuté il est détruits. En tant que tel, le service devrait s'arrêter de lui-même lorsque son travail est fait en appelant stopSelf(), ou à un autre composant pour l'arrêter l'appel de stopService().
Attention: Il est important que votre application cesse de ses services lorsqu'elle est faite de travail, afin d'éviter gaspiller les ressources du système et de consommer de l'énergie de la batterie. Si nécessaire, d'autres composants peuvent arrêter le service en appelant stopService(). Même si vous activez la liaison pour le service, vous devez toujours arrêter le - même si elle l'a jamais reçu un appel à onStartCommand()

d'autre part, le document dit:

*START_NOT_STICKY* - Si le système supprime le service après onStartCommand() renvoie, ne pas recréer le service, à moins qu'il y sont en attente de l'avis d'intention d'offrir. C'est l'option la plus sûre pour éviter l'exécution de votre service lorsqu'il n'est pas nécessaire et lorsque votre application peut il suffit de redémarrer toutes les tâches.

Donc, après la lecture de ce document et de quelques expériences, je pense que le système de traite manuellement tué des services comme inachevée (est tombé en panne: @see W/ActivityManager(306): Scheduling restart of crashed service) et il redémarre en dépit de la valeur renvoyée par onStartCommand.


stopSelf() ou stopService() - pas de redémarrage, pourquoi pas si le travail est terminé ?

10voto

Joe Points 8976

Je crois que le problème ici est que, pour un Service qui a été commencé en appelant startService(), la comptabilité interne pour ses liaisons (affectée par bindService()/unbindService()) sont en quelque sorte à prévenir le service pour être ramené correctement après il est prévu pour redémarrer lorsque le processus est tué.

Selon votre préférence, il semble qu'il y a trois solutions pour éviter ce problème (vous avez mentionné les deux premiers déjà dans votre question):

  • Utiliser startService()/stopService() seulement, ne pas utiliser bindService()/unbindService() .

  • Utiliser bindService()/unbindService() seulement ( BIND_AUTO_CREATE), ne pas utiliser startService()/stopService() .

  • Si vous avez besoin d' startService()/stopService() et bindService()/unbindService(), remplacer l' onUnbind() méthode dans votre service et appelez - stopSelf() partir de:

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

De mon test:

  • À l'aide de la première alternative de l'impression de cette ligne dans le journal:

    W/ActivityManager( nnn): le calendrier de redémarrage de s'est écrasé service de xxxx en 5000ms

    mais le service ne sera pas vraiment redémarré après que.

  • La deuxième solution sera la cause de votre service à être détruits après vous séparer (dans l'Activité, en onStop() dans l'exemple).

  • La troisième alternative est essentiellement de rendre votre code se comporte comme la deuxième alternative (sans avoir besoin de le changer par beaucoup).

Espérons que cette aide!

2voto

full.stack.ex Points 1563

Comment au sujet de reconsidérer votre modèle?

Le START_NOT_STICKY drapeau fait son apparition au cours de l'évolution de la technologie Android (après 1,6?). Très probablement, vous êtes face à certains subtil de régression dans le service de cycle de vie, qui a été introduit à l'époque. Donc, même si tout aussi subtile solution est trouvée pour l'instant - et comme par magie vérifié pour fonctionner sur tous les périphériques possibles - il ne va pas nécessairement de travail en vertu de nouvelles versions d'Android.

De toute façon, que le cycle de vie modèle semble être une anomalie, et que peut-être la raison même de votre course dans cette exotique problème.

Nous allons considérer deux cas.

  1. Normal (?). Certains le début de l'activité du service. Peu de temps après, il reçoit de la reliure, employé, indépendant - alors quoi? Arrêté en quelque sorte? Comment se fait-il n'y a pas significative de la consommation de la batterie dans ce cas?

  2. Anormale. Service tués à certains (aléatoire?) point. Lors du redémarrage, il met en œuvre certains hypothèse erronée et qu'il reste actif, faire quelque chose qui draine la batterie?

Il semble plutôt étrange.

Je voudrais analyser le processus simultanés dans votre produit. Tous les cas importants. Certains diagrammes, etc. Plus probablement, par conséquent, le besoin d'un tel modèle serait éliminé.

Sinon, il ressemble à n'importe quelle solution de contournement sera un fragile hack.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X