Question Comment vérifier si un service fonctionne sur Android?


Comment puis-je vérifier si un service d'arrière-plan (sur Android) est en cours d'exécution?

Je veux une activité Android qui permute l'état du service - il me permet de l'activer s'il est éteint et éteint s'il est allumé.


794
2018-03-01 18:09


origine


Réponses:


J'ai eu le même problème il n'y a pas longtemps. Comme mon service était local, j'ai fini par utiliser simplement un champ statique dans la classe de service pour basculer l'état, comme décrit par hackbod ici

EDIT (pour le compte rendu):

Voici la solution proposée par hackbod:

Si votre code client et serveur fait partie du même .apk et que vous êtes   liaison avec le service avec une intention concrète (celle qui spécifie   classe de service exacte), vous pouvez simplement avoir votre service   variable globale lorsqu'elle est en cours d'exécution que votre client peut vérifier.

Nous n'avons délibérément pas d'API pour vérifier si un service est   en cours d'exécution parce que, presque sans faute, quand vous voulez faire quelque chose   comme ça, vous vous retrouvez avec des conditions de course dans votre code.


248
2018-03-03 22:58



J'utilise ce qui suit à l'intérieur d'une activité:

private boolean isMyServiceRunning(Class<?> serviceClass) {
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (serviceClass.getName().equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

Et je l'appelle en utilisant:

isMyServiceRunning(MyService.class)

Cela fonctionne de manière fiable, car il est basé sur les informations sur l'exécution des services fournis par le système d'exploitation Android à travers ActivityManager # getRunningServices.

Toutes les approches utilisant des événements onDestroy ou onSometing ou des binders ou des variables statiques ne fonctionneront pas de manière fiable car en tant que développeur, vous ne savez jamais, quand Android décide de tuer votre processus ou lequel des callbacks mentionnés sont appelés ou non. Veuillez noter la colonne "killable" dans le table des événements du cycle de vie dans la documentation Android.


1518
2018-05-07 12:54



Je l'ai!

Toi DOIT appel startService() pour que votre service soit correctement enregistré et passe BIND_AUTO_CREATE ne suffira pas.

Intent bindIntent = new Intent(this,ServiceTask.class);
startService(bindIntent);
bindService(bindIntent,mConnection,0);

Et maintenant la classe ServiceTools:

public class ServiceTools {
    private static String LOG_TAG = ServiceTools.class.getName();

    public static boolean isServiceRunning(String serviceClassName){
        final ActivityManager activityManager = (ActivityManager)Application.getContext().getSystemService(Context.ACTIVITY_SERVICE);
        final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);

        for (RunningServiceInfo runningServiceInfo : services) {
            if (runningServiceInfo.service.getClassName().equals(serviceClassName)){
                return true;
            }
        }
        return false;
     }
}

67
2018-02-12 04:42



Un petit complément est:

Mon but est de savoir si un service est en cours d'exécution sans réellement l'exécuter s'il n'est pas en cours d'exécution.

Appeler bindService ou appeler une intention qui peut être interceptée par le service n'est pas une bonne idée, car il démarrera le service s'il n'est pas en cours d'exécution.

Ainsi, comme l'a suggéré miracle2k, le mieux est d'avoir un champ statique dans la classe de service pour savoir si le service a été démarré ou non.

Pour le rendre encore plus propre, je suggère de transformer le service en un singleton avec un chargement très très paresseux: c'est-à-dire, il n'y a pas d'instanciation du tout singleton instance par des méthodes statiques. La méthode statique getInstance de votre service / singleton renvoie simplement l'instance du singleton s'il a été créé. Mais il ne commence pas ou instancie le singleton lui-même. Le service est démarré uniquement via des méthodes de démarrage de service normales.

Il serait alors encore plus simple de modifier le motif de conception singleton pour renommer la méthode getInstance confuse en quelque chose comme le isInstanceCreated() : boolean méthode.

Le code ressemblera à:

public class MyService extends Service
{
   private static MyService instance = null;

   public static boolean isInstanceCreated() {
      return instance != null;
   }//met

   @Override
   public void onCreate()
   {
      instance = this;
      ....
   }//met

   @Override
   public void onDestroy()
   {
      instance = null;
      ...
   }//met
}//class

Cette solution est élégante, mais elle n'est pertinente que si vous avez accès à la classe de service et uniquement pour les classes à côté de l'application / package du service. Si vos classes sont en dehors de l'application / du package de service, vous pouvez interroger ActivityManager avec les limitations soulignées par Pieter-Jan Van Robays.


51
2018-05-20 10:58



Vous pouvez l'utiliser (je n'ai pas encore essayé, mais j'espère que cela fonctionne):

if(startService(someIntent) != null) {
    Toast.makeText(getBaseContext(), "Service is already running", Toast.LENGTH_SHORT).show();
}
else {
    Toast.makeText(getBaseContext(), "There is no service running, starting service..", Toast.LENGTH_SHORT).show();
}

La méthode startService renvoie un objet ComponentName s'il existe déjà un service. Sinon, null sera retourné.

Voir public abstract ComponentName startService (service d'intention).

Ce n'est pas comme si je vérifiais, parce que ça commence le service, donc vous pouvez ajouter stopService(someIntent); sous le code.


22
2017-09-23 17:01



    public boolean checkServiceRunning(){
         ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) 
                {
                    if ("com.example.yourpackagename.YourServiceName"
                            .equals(service.service.getClassName())) 
                    {
                        return true;
                    }
                }
             return false;
    }

13
2017-07-14 03:23



J'ai légèrement modifié l'une des solutions présentées ci-dessus, mais en passant la classe au lieu d'un nom de chaîne générique, afin de pouvoir comparer les chaînes issues de la même méthode class.getName()

public class ServiceTools {
    private static String LOG_TAG = ServiceTools.class.getName();

    public static boolean isServiceRunning(Context context,Class<?> serviceClass){
        final ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
        final List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);

        for (RunningServiceInfo runningServiceInfo : services) {
            Log.d(Constants.TAG, String.format("Service:%s", runningServiceInfo.service.getClassName()));
            if (runningServiceInfo.service.getClassName().equals(serviceClass.getName())){
                return true;
            }
        }
        return false;
    }
}

et alors

Boolean isServiceRunning = ServiceTools.isServiceRunning(
                    MainActivity.this.getApplicationContext(),
                    BackgroundIntentService.class);

11
2017-07-13 14:26



Je veux juste ajouter une note à la réponse par @Snicolas. Les étapes suivantes peuvent être utilisées pour vérifier le service d'arrêt avec / sans appel onDestroy().

  1. onDestroy() appelé: Accédez à Paramètres -> Application -> Services en cours d'exécution -> Sélectionnez et arrêtez votre service.

  2. onDestroy() Non appelé: Accédez à Paramètres -> Application -> Gérer les applications -> Sélectionnez et "Forcer l'arrêt" de votre application dans laquelle votre service est en cours d'exécution. Cependant, comme votre application est arrêtée ici, les instances de service seront définitivement arrêtées.

Enfin, je voudrais mentionner que l'approche mentionnée ici en utilisant une variable statique dans la classe singleton fonctionne pour moi.


7
2017-09-16 16:54



onDestroy n'est pas toujours appelé dans le service donc c'est inutile!

Par exemple: Il suffit de lancer à nouveau l'application avec un changement d'Eclipse. L'application est résolue en utilisant SIG: 9.


6
2018-02-12 04:26



Ceci est un extrait de Android docs

Diffusions commandées (envoyées avec Context.sendOrderedBroadcast) sont   livré à un récepteur à la fois. Comme chaque récepteur s'exécute dans   tour, il peut propager un résultat au prochain récepteur, ou il peut   annuler complètement l'émission afin qu'elle ne soit pas transmise à d'autres   récepteurs.

Pensez à ce hack comme "ping" le Service puisque nous pouvons diffuser de façon synchrone, nous pouvons diffuser et obtenir un résultat synchrone sur le fil de l'interface utilisateur.

Service

BroadcastReceiver .

@Override
public void onCreate() {
   LocalBroadcastManager
     .getInstance(this)
     .registerReceiver(new ServiceEchoReceiver(), IntentFilter("echo");
}

private class ServiceEchoReceiver{
    public void onReceive (Context context, Intent intent) {
      LocalBroadcastManager
         .getInstance(this)
         .sendBroadcastSync(new Intent("echo"));
    }
}

Activity

    bool serviceRunning = false;

    protected void onCreate (Bundle savedInstanceState){
        LocalBroadcastManager.getInstance(this).registerReceiver(echo);
        LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("echo"));
        if(!serviceRunning){
           //try and run the service
        }
    }

    private BroadcastReceiver echo = new BroadcastReceiver(){
        public void onReceive (Context context, Intent intent) {
          serviceRunning = true;   
        }
    }

6
2017-09-19 17:48