Comment vérifier si un service fonctionne sur Android?


936

Comment vérifier si un service d'arrière-plan est en cours d'exécution?

Je veux une activité Android qui permute l'état du service - cela me permet de l'activer s'il est désactivé et de le désactiver s'il est activé.



17
la bonne réponse est ci-dessous et non celle marquée: stackoverflow.com/a/5921190/2369122
toidiu

1
@toidiu Si cela n'a pas déjà été nerfé getRunningTasks(), ce sera probablement le cas.
Kevin Krumwiede

en utilisant la fonction getSystemService (), vous pouvez récupérer tous les services en cours d'exécution. parcourez-le et vérifiez que votre service existe dans la liste ici, vous pouvez voir un petit exemple wiki.workassis.com/android-check-the-service-is-running
Bikesh M

Réponses:


292

J'ai eu le même problème il n'y a pas longtemps. Étant donné que 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 mémoire):

Voici la solution proposée par hackbod:

Si votre code client et serveur fait partie du même .apk et que vous vous connectez au service avec une intention concrète (qui spécifie la classe de service exacte), vous pouvez simplement demander à votre service de définir une variable globale lors de son exécution. 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 car, presque sans échec, lorsque vous voulez faire quelque chose comme ça, vous vous retrouvez avec des conditions de concurrence critique dans votre code.


27
@Pacerier, la solution à laquelle vous faites référence nécessite de démarrer le service et je pense que la meilleure solution flexible devrait vous permettre de vérifier si un service fonctionne sans le démarrer.
Tom

17
Qu'en est-il si le service est arrêté par le système, comment le détectez-vous et basculez-vous votre variable?
jmng

23
Lorsque l'application est supprimée, le service qu'elle avait démarré est également supprimé, mais le service onDestroy()n'est pas appelé. La variable statique ne peut donc pas être mise à jour dans un tel scénario entraînant un comportement incohérent.
faizal

5
@faizal La variable statique ne serait-elle pas également ré-initialisée, la redéfinissant ainsi à la valeur par défaut qui indique que le service n'est plus en cours d'exécution?
PabloC

12
@faizal, le service local n'est pas un processus distinct, donc si le service est tué, l'application va également tuer.
Sever

1674

J'utilise les éléments suivants à 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 via ActivityManager # getRunningServices .

Toutes les approches utilisant des événements onDestroy ou onSometing ou des classeurs 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 quels rappels mentionnés sont appelés ou non. Veuillez noter la colonne "killable" dans le tableau des événements du cycle de vie dans la documentation Android.


85
Merci pour cette solution. Je voudrais ajouter: "com.example.MyService" est plus élégant pour utiliser MyService.class.getName ()
peter.bartos

10
Personnellement, j'ai choisi d'utiliser un champ statique. Bien que l'utilisation de getRunningServices () soit une solution plus robuste, je pense qu'il y a dans ces deux solutions un compromis entre robustesse et efficacité / simplicité. Si vous devez vérifier fréquemment si un service est en cours d'exécution, parcourir en boucle plus de 30 services en cours d'exécution n'est pas très idéal. Le cas rare d'un service détruit par le système peut être géré par un bloc try / catch ou en utilisant START_STICKY.
robguinness

80
Non, ce n'est pas la bonne réponse car elle est également écrite dans la documentation: "Remarque: cette méthode est uniquement destinée au débogage ou à la mise en œuvre d'interfaces utilisateur de type gestion de service." Ce n'est pas destiné au contrôle du flux!
seb

40
Les gens trouvent élégant d'avoir à passer par tout cela pour vérifier si un serveur fonctionne?
Rui Marques

81
A partir O Android , getRunningServicesest dépréciée. Cette réponse nécessite une mise à jour pour une version plus récente.
poring91

75

Je l'ai!

Vous DEVEZ appeler startService()pour que votre service soit correctement enregistré et le passage BIND_AUTO_CREATEne 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;
     }
}

Cela répertoriera uniquement les services système, non?! Donc, mon service local est exclu de la liste et je vais me tromper; (
Ewoks

Cela fonctionne avec les services externes, car les services locaux sont assez évidents si vous exécutez.
Kevin Parker

11
Désolé mais je dois dire que c'est une réponse super idiote .. Pourquoi est-ce super évident?!
Ewoks

10
Je ne sais pas ce que tu veux dire ici ... Qui parlait du crash du tout?! Je ne suis pas intéressé à l'écraser. Le service peut être démarré, arrêté, c'était peut-être un service intentionnel et il s'arrêtera de lui-même quand il sera fait ... La question est de savoir s'il fonctionne toujours ou non après 3 min par exemple.
Ewoks

1
Il est incorrect de donner l'impression qu'un service lié doit également être démarré. NON. La création automatique de liens fait exactement ce qu'elle dit. Il va créer (et donc "démarrer") le service si le service n'est pas encore en cours d'exécution.
Sreedevi J

57

Un petit complément est:

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

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 une extraction très très paresseuse: c'est-à-dire qu'il n'y a aucune instanciation du tout de l' instance de singleton 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 démarre ni n'instancie réellement le singleton lui-même. Le service est démarré uniquement via les méthodes normales de démarrage du service.

Il serait alors encore plus propre de modifier le modèle de conception singleton pour renommer la méthode getInstance déroutante en quelque chose comme la isInstanceCreated() : booleanmé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 / package de service, vous pouvez interroger le ActivityManager avec des limitations soulignées par Pieter-Jan Van Robays.


32
C'est imparfait. onDestroy n'est pas garanti d'être appelé.
Pacerier

8
Lorsque le système manque de mémoire, votre service sera automatiquement supprimé sans appel à votre onDestroy, c'est pourquoi je dis que cela est défectueux.
Pacerier

17
@Pacerier, mais si le système tue le processus, l'indicateur d'instance sera toujours réinitialisé. Je suppose que lorsque le récepteur sera chargé (après que le système a tué le service), l'indicateur statique "instance" sera recréé comme nul.
Tom

2
Au moins mieux que d'itérer à travers tous ces services dans isMyServiceRunning qui retarde vraiment les choses si cela est fait à chaque rotation de l'appareil :)
Gunnar Forsgren - Mobimation

1
Votre variable d'instance ne doit pas être déclarée finale, sinon elle ne peut pas être définie ou nullée par les méthodes onCreate () ou onDestroy ().
k2col

27

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 un service déjà en cours d'exécution. Sinon, null sera retourné.

Voir le résumé public ComponentName startService (service d'intention) .

Ce n'est pas comme vérifier, je pense, car cela démarre le service, vous pouvez donc ajouter stopService(someIntent);sous le code.


11
Pas exactement ce que disent les docs. Selon votre lien: "Renvoie Si le service est en cours de démarrage ou est déjà en cours d'exécution, le ComponentName du service réel qui a été démarré est renvoyé; sinon, si le service n'existe pas, null est renvoyé."
Gabriel

Belle pensée ... mais ne correspond pas à la situation actuelle.
Code_Life

5
ce n'est pas la bonne façon, car lorsque le déclencheur IDE if(startService(someIntent) != null)vérifiera cela, IsserviceRunningmais cela jouera également un nouveau service.
Chintan Khetiya

Comme il est indiqué, si vous arrêtez le service après ce contrôle, il sera utile pour ce problème. Mais pourquoi démarrer et arrêter un service pour rien?
Taner

6
cela va démarrer le service, n'est-ce pas? Je veux juste vérifier l'état du service au lieu de le démarrer ...
Raptor

26
/**
 * Check if the service is Running 
 * @param serviceClass the class of the Service
 *
 * @return true if the service is running otherwise false
 */
public boolean checkServiceRunning(Class<?> serviceClass){
    ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE))
    {
        if (serviceClass.getName().equals(service.service.getClassName()))
        {
            return true;
        }
    }
    return false;
}

21

Un extrait de documents Android :

Comme sendBroadcast (Intent) , mais s'il y a des récepteurs pour Intent, cette fonction les bloquera et les enverra immédiatement avant de revenir.

Pensez à ce hack comme "cinglant" leService . Puisque nous pouvons diffuser de manière synchrone, nous pouvons diffuser et obtenir un résultat de manière synchrone , sur le thread d'interface utilisateur.

Service

@Override
public void onCreate() {
   LocalBroadcastManager
     .getInstance(this)
     .registerReceiver(new ServiceEchoReceiver(), new IntentFilter("ping"));
     //do not forget to deregister the receiver when the service is destroyed to avoid
     //any potential memory leaks 
}

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

Activity

    bool serviceRunning = false;

    protected void onCreate (Bundle savedInstanceState){
        LocalBroadcastManager.getInstance(this).registerReceiver(pong, new IntentFilter("pong"));
        LocalBroadcastManager.getInstance(this).sendBroadcastSync(new Intent("ping"));
        if(!serviceRunning){
           //run the service
        }
    }

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

Le gagnant dans de nombreuses applications est, bien sûr, un champ booléen statique sur le service qui est défini sur truein Service.onCreate()et sur falsein Service.onDestroy()car il est beaucoup plus simple.


C'est une bien meilleure solution que celle acceptée, qui échoue si Android tue le service car la méthode de variable globale indiquerait toujours que le service est en cours d'exécution alors qu'il ne l'est plus. Cette astuce de diffusion de ping-pong synchrone est en fait la seule méthode fiable pour vérifier si un service est actif. À lui seul, il vous suffit de DEMANDER le service s'il existe. S'il répond, le service est actif et en cours d'exécution, sinon il n'a pas été démarré ou a été arrêté, soit par programme, soit par le système pour récupérer de la mémoire.
Phoenix dévoilé

13

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 d'être sûr de comparer les chaînes provenant 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);

pour être plus strict, vous pouvez changer le Class<? extends Service>
paramètre de

11

La bonne façon de vérifier si un service est en cours d'exécution est de simplement le demander. Implémentez un BroadcastReceiver dans votre service qui répond aux pings de vos activités. Enregistrez le BroadcastReceiver au démarrage du service et annulez-le lorsque le service est détruit. Depuis votre activité (ou n'importe quel composant), envoyez une intention de diffusion locale au service et s'il répond, vous savez qu'il est en cours d'exécution. Notez la différence subtile entre ACTION_PING et ACTION_PONG dans le code ci-dessous.

public class PingableService extends Service
{
    public static final String ACTION_PING = PingableService.class.getName() + ".PING";
    public static final String ACTION_PONG = PingableService.class.getName() + ".PONG";

    public int onStartCommand (Intent intent, int flags, int startId)
    {
        LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(ACTION_PING));
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy ()
    {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
        super.onDestroy();
    }

    private BroadcastReceiver mReceiver = new BroadcastReceiver()
    {
        @Override
        public void onReceive (Context context, Intent intent)
        {
            if (intent.getAction().equals(ACTION_PING))
            {
                LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
                manager.sendBroadcast(new Intent(ACTION_PONG));
            }
        }
    };
}


public class MyActivity extends Activity
{
    private boolean isSvcRunning = false;

    @Override
    protected void onStart()
    {
        LocalBroadcastManager manager = LocalBroadcastManager.getInstance(getApplicationContext());
        manager.registerReceiver(mReceiver, new IntentFilter(PingableService.ACTION_PONG));
        // the service will respond to this broadcast only if it's running
        manager.sendBroadcast(new Intent(PingableService.ACTION_PING));
        super.onStart();
    }

    @Override
    protected void onStop()
    {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
        super.onStop();
    }

    protected BroadcastReceiver mReceiver = new BroadcastReceiver()
    {
        @Override
        public void onReceive (Context context, Intent intent)
        {
            // here you receive the response from the service
            if (intent.getAction().equals(PingableService.ACTION_PONG))
            {
                isSvcRunning = true;
            }
        }
    };
}

1
J'aime vraiment cette approche. C'est un peu lourd en code mais ça marchera toujours. Je ne pense pas que les intentions de diffusion soient dépréciées de sitôt :)
ShellDude

8

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

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

  2. onDestroy()non appelé: allez dans Paramètres -> Application -> Gérer les applications -> Sélectionnez et "Forcer l'arrêt" de votre application dans laquelle votre service s'exécute. Cependant, comme votre application est arrêtée ici, les instances de service seront également 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

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

Par exemple: il suffit de relancer l'application avec un changement depuis Eclipse. L'application est fermée de force en utilisant SIG: 9.


6

Tout d'abord, vous ne devez pas essayer d'accéder au service en utilisant ActivityManager. (Discuté ici )

Les services peuvent fonctionner seuls, être liés à une activité ou aux deux. La façon de vérifier dans une activité si votre service est en cours d'exécution ou non consiste à créer une interface (qui étend Binder) dans laquelle vous déclarez des méthodes que les deux, l'activité et le service, comprennent. Vous pouvez le faire en créant votre propre interface où vous déclarez par exemple "isServiceRunning ()". Vous pouvez ensuite lier votre activité à votre service, exécuter la méthode isServiceRunning (), le service vérifiera s'il s'exécute ou non et renvoie un booléen à votre activité.

Vous pouvez également utiliser cette méthode pour arrêter votre service ou interagir avec lui d'une autre manière.

J'ai utilisé ce tutoriel pour apprendre à implémenter ce scénario dans mon application.


3
Cette discussion a eu lieu le '12 / 26/07 '. Soit c'est juillet de cette année (c'est-à-dire dans le futur), soit c'est avant qu'Android ne soit même public. Quoi qu'il en soit, cela ne me fait pas confiance.
Tom

Cette discussion date du 26 décembre 2007. Ils discutent d'une version préliminaire, je pense ( developer.android.com/sdk/OLD_RELEASENOTES.html#m3-rc37a ) qui a été publiée le 14 décembre 2007.
ingh.am

6

Encore une fois, une autre alternative que les gens pourraient trouver plus propre s'ils utilisent des intentions en attente (par exemple avec AlarmManager:

public static boolean isRunning(Class<? extends Service> serviceClass) {
    final Intent intent = new Intent(context, serviceClass);
    return (PendingIntent.getService(context, CODE, intent, PendingIntent.FLAG_NO_CREATE) != null);
}

Où se CODEtrouve une constante que vous définissez en privé dans votre classe pour identifier les intentions en attente associées à votre service.


1
Combinez ou mettez à jour votre réponse précédente. Veuillez ne pas publier plus d'une réponse par message.
ChuongPham

Cette réponse peut-elle être développée, c'est-à-dire comment associer la valeur de CODE au service?
Dave Nottage

Où trouver le contexte?
basilic

6

Vous trouverez ci-dessous un hack élégant qui couvre tous les Ifs. Ceci est réservé aux services locaux.

    public final class AService extends Service {

        private static AService mInstance = null;

        public static boolean isServiceCreated() {
            try {
                // If instance was not cleared but the service was destroyed an Exception will be thrown
                return mInstance != null && mInstance.ping();
            } catch (NullPointerException e) {
                // destroyed/not-started
                return false;
            }
        }

        /**
         * Simply returns true. If the service is still active, this method will be accessible.
         * @return
         */
        private boolean ping() {
            return true;
        }

        @Override
        public void onCreate() {
            mInstance = this;
        }

        @Override
        public void onDestroy() {
            mInstance = null;
        }
    }

Et puis plus tard:

    if(AService.isServiceCreated()){
        ...
    }else{
        startService(...);
    }

Le seul problème avec cela est que le service est un service Sticky et qu'il redémarre lui-même. L'appel à isServiceCreated () retournera false après le redémarrage du service car mInstance sera null.
Mira_Cole

1
OnCreate ne serait-il pas alors appelé lorsque le service redémarrerait?
TheRealChx101

6

Version Xamarin C #:

private bool isMyServiceRunning(System.Type cls)
{
    ActivityManager manager = (ActivityManager)GetSystemService(Context.ActivityService);

    foreach (var service in manager.GetRunningServices(int.MaxValue)) {
        if (service.Service.ClassName.Equals(Java.Lang.Class.FromType(cls).CanonicalName)) {
            return true;
        }
    }
    return false;
}

Vous avez besoin du «Contexte» pour GetSystemService.
test du

5

Pour le cas d'utilisation donné ici, nous pouvons simplement utiliser la stopService()valeur de retour de la méthode. Il retourne trues'il existe le service spécifié et il est tué. Sinon, il revient false. Vous pouvez donc redémarrer le service si le résultat est falsesinon il est assuré que le service actuel a été arrêté. :) Il serait mieux si vous avez un oeil à ce .


5

Une autre approche utilisant kotlin. Inspiré des réponses des autres utilisateurs

fun isMyServiceRunning(serviceClass: Class<*>): Boolean {
    val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    return manager.getRunningServices(Integer.MAX_VALUE)
            .any { it.service.className == serviceClass.name }
}

Comme extension kotlin

fun Context.isMyServiceRunning(serviceClass: Class<*>): Boolean {
    val manager = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    return manager.getRunningServices(Integer.MAX_VALUE)
            .any { it.service.className == serviceClass.name }
}

Usage

context.isMyServiceRunning(MyService::class.java)

4

Dans kotlin, vous pouvez ajouter une variable booléenne dans l'objet compagnon et vérifier sa valeur dans la classe de votre choix:

companion object{
     var isRuning = false

}

Changer sa valeur lorsque le service est créé et détruit

 override fun onCreate() {
        super.onCreate()
        isRuning = true
    }

override fun onDestroy() {
    super.onDestroy()
    isRuning = false
    }

3

Dans votre sous-classe de service, utilisez un booléen statique pour obtenir l'état du service, comme illustré ci-dessous.

MyService.kt

class MyService : Service() {
    override fun onCreate() {
        super.onCreate()
        isServiceStarted = true
    }
    override fun onDestroy() {
        super.onDestroy()
        isServiceStarted = false
    }
    companion object {
        var isServiceStarted = false
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val serviceStarted = FileObserverService.isServiceStarted
        if (!serviceStarted) {
            val startFileObserverService = Intent(this, FileObserverService::class.java)
            ContextCompat.startForegroundService(this, startFileObserverService)
        }
    }
}

3

Pour kotlin, vous pouvez utiliser le code ci-dessous.

fun isMyServiceRunning(calssObj: Class<SERVICE_CALL_NAME>): Boolean {
    val manager = requireActivity().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
        if (calssObj.getName().equals(service.service.getClassName())) {
            return true
        }
    }
    return false
}

C'est une excellente réponse pour écrire des tests, car vous pouvez l'utiliser sans changer votre code de travail.
Robert Liberatore

2

La réponse de geekQ mais dans la classe Kotlin. Merci geekQ

fun isMyServiceRunning(serviceClass : Class<*> ) : Boolean{
    var manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    for (service in manager.getRunningServices(Integer.MAX_VALUE)) {
        if (serviceClass.name.equals(service.service.className)) {
            return true
        }
    }
    return false
}

L'appel

isMyServiceRunning(NewService::class.java)

6
ActivityManager.getRunningServicesest déconseillé depuis Android O
Daniel Shatz

1

Il peut y avoir plusieurs services avec le même nom de classe.

Je viens de créer deux applications. Le nom du package de la première application est com.example.mock. J'ai créé un sous-package appelé loremdans l'application et un service appelé Mock2Service. Son nom complet est donc com.example.mock.lorem.Mock2Service.

J'ai ensuite créé la deuxième application et un service appelé Mock2Service. Le nom du package de la deuxième application est com.example.mock.lorem. Le nom complet du service l'est com.example.mock.lorem.Mock2Serviceégalement.

Voici ma sortie logcat.

03-27 12:02:19.985: D/TAG(32155): Mock-01: com.example.mock.lorem.Mock2Service
03-27 12:02:33.755: D/TAG(32277): Mock-02: com.example.mock.lorem.Mock2Service

Une meilleure idée est de comparer les ComponentNameinstances en raison equals()de la ComponentNamecomparaison des noms de package et des noms de classe. Et il ne peut pas y avoir deux applications avec le même nom de package installées sur un appareil.

La méthode equals () de ComponentName.

@Override
public boolean equals(Object obj) {
    try {
        if (obj != null) {
            ComponentName other = (ComponentName)obj;
            // Note: no null checks, because mPackage and mClass can
            // never be null.
            return mPackage.equals(other.mPackage)
                    && mClass.equals(other.mClass);
        }
    } catch (ClassCastException e) {
    }
    return false;
}

Nom du composant


1

Veuillez utiliser ce code.

if (isMyServiceRunning(MainActivity.this, xyzService.class)) { // Service class name
    // Service running
} else {
    // Service Stop
}


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

0

Cela s'applique davantage au débogage du service d'intention car ils génèrent un thread, mais peuvent également fonctionner pour les services réguliers. J'ai trouvé ce fil grâce à Binging

Dans mon cas, j'ai joué avec le débogueur et trouvé la vue du thread. Cela ressemble à l'icône de puce dans MS Word. Quoi qu'il en soit, vous n'avez pas besoin d'être en mode débogueur pour l'utiliser. Cliquez sur le processus et cliquez sur ce bouton. Tous les services d'intention s'afficheront pendant leur exécution, au moins sur l'émulateur.


0

Si le service appartient à un autre processus ou APK, utilisez la solution basée sur ActivityManager.

Si vous avez accès à sa source, utilisez simplement la solution basée sur un champ statique. Mais à la place, en utilisant un booléen, je suggère d'utiliser un objet Date. Pendant que le service est en cours d'exécution, mettez simplement à jour sa valeur sur «maintenant» et quand il a terminé, définissez-la sur null. À partir de l'activité, vous pouvez vérifier si sa valeur nulle ou la date est trop ancienne, ce qui signifie qu'elle n'est pas en cours d'exécution.

Vous pouvez également envoyer une notification de diffusion à partir de votre service pour indiquer que des informations supplémentaires telles que la progression sont affichées.


0

À l'intérieur de TheServiceClass, définissez:

 public static Boolean serviceRunning = false;

Puis dans onStartCommand (...)

 public int onStartCommand(Intent intent, int flags, int startId) {

    serviceRunning = true;
    ...
}

 @Override
public void onDestroy()
{
    serviceRunning = false;

} 

Ensuite, appelez if(TheServiceClass.serviceRunning == true)de n'importe quelle classe.


4
Cela ne fonctionne pas si votre service est tué par Android.
Heisenberg

@Heisenberg Je viens de vivre ça moi-même. Tu sais pourquoi pas?
Tim

@Heisenberg lorsque mon application est tuée par le système d'exploitation, le service redémarre et définit le booléen statique sur vrai, mais en l'obtenant, il signale faux
Tim

cela ne fonctionnera pas si vous appelez stopService. Au moins pour les services d'intention. onDestroy()sera appelé immédiatement, mais onHandleIntent()sera toujours en cours d'exécution
serggl

1
@Heisenberg Ne tuera pas le service en raison d'une mémoire insuffisante signifie également tuer le processus?
développeur Android

0

utilisation simple bind avec Don't create auto - voir ps. et mettre à jour ...

public abstract class Context {

 ... 

  /*
  * @return {true} If you have successfully bound to the service, 
  *  {false} is returned if the connection is not made 
  *  so you will not receive the service object.
  */
  public abstract boolean bindService(@RequiresPermission Intent service,
        @NonNull ServiceConnection conn, @BindServiceFlags int flags);

exemple :

    Intent bindIntent = new Intent(context, Class<Service>);
    boolean bindResult = context.bindService(bindIntent, ServiceConnection, 0);

pourquoi ne pas utiliser? getRunningServices ()

List<ActivityManager.RunningServiceInfo> getRunningServices (int maxNum)
Return a list of the services that are currently running.

Remarque: cette méthode est uniquement destinée au débogage ou à la mise en œuvre d'interfaces utilisateur de type gestion de service.


ps. la documentation Android est trompeuse j'ai ouvert un problème sur google tracker pour éliminer les doutes:

https://issuetracker.google.com/issues/68908332

comme nous pouvons le voir, le service de liaison appelle réellement une transaction via le classeur ActivityManager via les liants de cache de service - je vérifie quel service est responsable de la liaison, mais comme nous pouvons le voir, le résultat de la liaison est:

int res = ActivityManagerNative.getDefault().bindService(...);
return res != 0;

la transaction se fait via un classeur:

ServiceManager.getService("activity");

suivant:

  public static IBinder getService(String name) {
    try {
        IBinder service = sCache.get(name);
        if (service != null) {
            return service;
        } else {
            return getIServiceManager().getService(name);

cela est défini dans ActivityThread via:

 public final void bindApplication(...) {

        if (services != null) {
            // Setup the service cache in the ServiceManager
            ServiceManager.initServiceCache(services);
        }

ceci est appelé dans ActivityManagerService dans la méthode:

 private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {
    ...
    thread.bindApplication(... , getCommonServicesLocked(),...)

puis:

 private HashMap<String, IBinder> getCommonServicesLocked() {

mais il n'y a pas de "fenêtre" d'activité et d'alarme seulement.

nous devons donc revenir pour appeler:

 return getIServiceManager().getService(name);

    sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());

cela fait appel à travers:

    mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);

qui conduit à :

BinderInternal.getContextObject()

et c'est une méthode native ....

  /**
     * Return the global "context object" of the system.  This is usually
     * an implementation of IServiceManager, which you can use to find
     * other services.
     */
    public static final native IBinder getContextObject();

je n'ai pas le temps maintenant de creuser en c donc jusqu'à ce que je dissèque l'appel reste je suspends ma réponse.

mais le meilleur moyen de vérifier si le service est en cours d'exécution est de créer une liaison (si la liaison n'est pas créée, le service n'existe pas) - et d'interroger le service sur son état via la liaison (en utilisant un indicateur interne stocké sur son état).

mise à jour 23.06.2018

j'ai trouvé ceux intéressants:

/**
 * Provide a binder to an already-bound service.  This method is synchronous
 * and will not start the target service if it is not present, so it is safe
 * to call from {@link #onReceive}.
 *
 * For peekService() to return a non null {@link android.os.IBinder} interface
 * the service must have published it before. In other words some component
 * must have called {@link android.content.Context#bindService(Intent, ServiceConnection, int)} on it.
 *
 * @param myContext The Context that had been passed to {@link #onReceive(Context, Intent)}
 * @param service Identifies the already-bound service you wish to use. See
 * {@link android.content.Context#bindService(Intent, ServiceConnection, int)}
 * for more information.
 */
public IBinder peekService(Context myContext, Intent service) {
    IActivityManager am = ActivityManager.getService();
    IBinder binder = null;
    try {
        service.prepareToLeaveProcess(myContext);
        binder = am.peekService(service, service.resolveTypeIfNeeded(
                myContext.getContentResolver()), myContext.getOpPackageName());
    } catch (RemoteException e) {
    }
    return binder;
}

en bref :)

"Fournir un classeur à un service déjà lié. Cette méthode est synchrone et ne démarre pas le service cible s'il n'est pas présent."

public IBinder peekService (service Intent, StringolvedType, String callingPackage) lève RemoteException;

*

public static IBinder peekService(IBinder remote, Intent service, String resolvedType)
             throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken("android.app.IActivityManager");
    service.writeToParcel(data, 0);
    data.writeString(resolvedType);
    remote.transact(android.os.IBinder.FIRST_CALL_TRANSACTION+84, data, reply, 0);
    reply.readException();
    IBinder binder = reply.readStrongBinder();
    reply.recycle();
    data.recycle();
    return binder;
}

*


bindResult (valeur de retour de la méthode bindService) n'a pas la valeur false si le service n'est pas en cours d'exécution.
Shangeeth Sivan

0

Ma conversion en kotlin des ActivityManager::getRunningServicesréponses basées. Mettez cette fonction dans une activité-

private fun isMyServiceRunning(serviceClass: Class<out Service>) =
    (getSystemService(ACTIVITY_SERVICE) as ActivityManager)
        .getRunningServices(Int.MAX_VALUE)
        ?.map { it.service.className }
        ?.contains(serviceClass.name) ?: false

-2

Il vous est possible d'utiliser ces options à partir des options du développeur Android pour voir si votre service fonctionne toujours en arrière-plan.

1. Open Settings in your Android device.
2. Find Developer Options.
3. Find Running Services option.
4. Find your app icon.
5. You will then see all the service that belongs to your app running in the background.

-5

Allez-y doucement les gars ... :)

Je pense que la solution la plus appropriée consiste à détenir une paire clé-valeur pour SharedPreferencessavoir si le service fonctionne ou non.

La logique est très droite; à n'importe quel poste souhaité dans votre classe de service; mettez une valeur booléenne qui vous servira d'indicateur pour savoir si le service est en cours d'exécution ou non. Lisez ensuite cette valeur où vous le souhaitez dans votre application.

Un exemple de code que j'utilise dans mon application est ci-dessous:

Dans ma classe Service (Un service pour Audio Stream), j'exécute le code suivant lorsque le service est en service;

private void updatePlayerStatus(boolean isRadioPlaying)
{
        SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.str_shared_file_name), Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putBoolean(getString(R.string.str_shared_file_radio_status_key), isRadioPlaying);
        editor.commit();
}

Ensuite, dans toute activité de mon application, je vérifie l'état du service à l'aide du code suivant;

private boolean isRadioRunning() {
        SharedPreferences sharedPref = this.getSharedPreferences(getString(R.string.str_shared_file_name), Context.MODE_PRIVATE);

        return sharedPref.getBoolean(getString(R.string.str_shared_file_radio_status_key), false);
}

Pas d'autorisations spéciales, pas de boucles ... Facile, solution propre :)

Si vous avez besoin d'informations supplémentaires, veuillez vous référer au lien

J'espère que cela t'aides.


19
Seulement que personne ne mettra à jour la valeur pour vous quand ils tuent le service
Gunnar Forsgren - Mobimation

quand tuer le service onDestroy () sera déclenché et il est possible de mettre à jour son état
Jongz Puangput

5
@JongzPuangput, onDestroyn'est pas toujours appelé lorsque le service est tué. Par exemple, j'ai vu mes services tués dans des situations de mémoire faible sans onDestroyêtre appelé.
Sam

@Sam Alors, comment s'appellera-t-on?
Ruchir Baronia

2
@RuchirBaronia Pour autant que je me souvienne, vous n'êtes tout simplement pas averti lorsque vos affaires sont tuées. Je crois qu'Android est conçu pour tuer les applications selon les besoins, et les applications devraient être conçues pour s'attendre à être tuées à tout moment sans notification.
Sam
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.