Wie überprüfe ich, ob ein Dienst unter Android ausgeführt wird?

936

Wie überprüfe ich, ob ein Hintergrunddienst ausgeführt wird?

Ich möchte eine Android-Aktivität, die den Status des Dienstes umschaltet. Mit dieser Funktion kann ich sie aktivieren, wenn sie deaktiviert ist, und deaktivieren, wenn sie aktiviert ist.

Biene
quelle
2
Schauen Sie sich diesen deutschen Leitfaden an .
Markus Peröbner
17
Die richtige Antwort ist unten und nicht die markierte: stackoverflow.com/a/5921190/2369122
toidiu
1
@toidiu Wenn das noch nicht so nerfed wurde getRunningTasks(), wird es wahrscheinlich sein.
Kevin Krumwiede
Mit der Funktion getSystemService () können Sie alle laufenden Dienste abrufen. Durchlaufen Sie es und überprüfen Sie, ob Ihr Dienst in der Liste vorhanden ist. Hier sehen Sie ein kleines Beispiel wiki.workassis.com/android-check-the-service-is-running
Bikesh M

Antworten:

292

Ich hatte vor kurzem das gleiche Problem. Da mein Dienst lokal war, habe ich einfach ein statisches Feld in der Dienstklasse verwendet, um den Status umzuschalten, wie hier von hackbod beschrieben

EDIT (für die Aufzeichnung):

Hier ist die von Hackbod vorgeschlagene Lösung:

Wenn Ihr Client- und Servercode Teil derselben APK-Datei ist und Sie mit einer konkreten Absicht (eine, die die genaue Serviceklasse angibt) an den Service binden, können Sie Ihren Service einfach eine globale Variable festlegen lassen, wenn er diese ausführt Ihr Kunde kann dies überprüfen.

Wir haben absichtlich keine API, um zu überprüfen, ob ein Dienst ausgeführt wird, da Sie fast immer Rennbedingungen in Ihrem Code haben, wenn Sie so etwas tun möchten.

miracle2k
quelle
27
@Pacerier, die Lösung, auf die Sie verweisen, erfordert das Starten des Dienstes. Ich denke, die beste flexible Lösung sollte es Ihnen ermöglichen, zu überprüfen, ob ein Dienst ausgeführt wird, ohne ihn zu starten.
Tom
17
Was ist, wenn der Dienst vom System gestoppt wird? Wie erkennen Sie das und schalten Ihre Variable um?
28.
23
Wenn die App beendet wird, wird auch der Dienst, den sie gestartet hat, beendet, aber der Dienst onDestroy()wird nicht aufgerufen. Daher kann die statische Variable in einem solchen Szenario nicht aktualisiert werden, was zu inkonsistentem Verhalten führt.
Faizal
5
@faizal Würde die statische Variable nicht auch neu initialisiert und somit auf den Standardwert zurückgesetzt, der angibt, dass der Dienst nicht mehr ausgeführt wird?
PabloC
12
@faizal, lokaler Dienst ist kein separater Prozess. Wenn also der Dienst beendet wird, wird auch die App beendet.
Sever
1674

Ich verwende Folgendes innerhalb einer Aktivität:

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

Und ich nenne es mit:

isMyServiceRunning(MyService.class)

Dies funktioniert zuverlässig, da es auf den Informationen zum Ausführen von Diensten basiert, die vom Android-Betriebssystem über ActivityManager # getRunningServices bereitgestellt werden .

Alle Ansätze, die onDestroy- oder onSometing-Ereignisse oder Ordner oder statische Variablen verwenden, funktionieren nicht zuverlässig, da Sie als Entwickler nie wissen, wann Android Ihren Prozess beendet oder welche der genannten Rückrufe aufgerufen werden oder nicht. Bitte beachten Sie die Spalte "killable" in der Lifecycle-Ereignistabelle in der Android-Dokumentation.

geekQ
quelle
85
Danke für diese Lösung. Ich möchte hinzufügen: Stattdessen ist "com.example.MyService" eleganter, um MyService.class.getName ()
peter.bartos
10
Persönlich habe ich ein statisches Feld verwendet. Obwohl die Verwendung von getRunningServices () eine robustere Lösung ist, glaube ich, dass diese beiden Lösungen einen Kompromiss zwischen Robustheit und Effizienz / Einfachheit aufweisen. Wenn Sie häufig überprüfen müssen, ob ein Dienst ausgeführt wird, ist das Durchlaufen von möglicherweise mehr als 30 ausgeführten Diensten nicht sehr ideal. Der seltene Fall, dass ein Dienst vom System zerstört wird, kann möglicherweise durch einen Try / Catch-Block oder mithilfe von START_STICKY behandelt werden.
Robguinness
80
Nein, es ist nicht die richtige Antwort, da es auch in den Dokumenten geschrieben steht: "Hinweis: Diese Methode ist nur zum Debuggen oder Implementieren von Benutzeroberflächen vom Typ Service Management vorgesehen." Es ist nicht für den Kontrollfluss gedacht!
22.
40
Die Leute finden es elegant, all das durchgehen zu müssen, um zu überprüfen, ob ein Server läuft?
Rui Marques
80
Starten Android OS , ist getRunningServicesveraltet. Diese Antwort benötigt ein Update für eine neuere Version.
Poring91
75

Ich habs!

Sie MÜSSEN anrufen, startService()damit Ihr Dienst ordnungsgemäß registriert wird und das Bestehen BIND_AUTO_CREATEnicht ausreicht.

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

Und jetzt die ServiceTools-Klasse:

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;
     }
}
Kevin Parker
quelle
Dies listet nur Systemdienste auf, nein?! Daher ist mein lokaler Dienst von der Liste ausgeschlossen und ich werde falsch liegen (
Ewoks
Dies funktioniert mit externen Diensten, da lokale Dienste beim Ausführen ziemlich offensichtlich sind.
Kevin Parker
11
Entschuldigung, aber ich muss sagen, das ist eine super dumme Antwort. Warum ist es super offensichtlich?!
Ewoks
10
Nicht klar, was du hier meinst ... Wer hat überhaupt über einen Absturz gesprochen?! Ich bin nicht daran interessiert, es zum Absturz zu bringen. Der Dienst kann gestartet, gestoppt werden, möglicherweise war es ein absichtlicher Dienst, und er wird von selbst beendet, wenn er abgeschlossen ist ... Die Frage ist, wie Sie beispielsweise nach 3 Minuten feststellen können, ob er noch ausgeführt wird oder nicht.
Ewoks
1
Es ist falsch, den Eindruck zu erwecken, dass auch ein gebundener Dienst gestartet werden muss. NEIN. Bind Auto Create macht genau das, was es sagt. Der Dienst wird erstellt (und daher "gestartet"), wenn der Dienst noch nicht ausgeführt wird.
Sreedevi J
57

Eine kleine Ergänzung ist:

Mein Ziel ist es zu wissen, ob ein Dienst ausgeführt wird, ohne ihn tatsächlich auszuführen, wenn er nicht ausgeführt wird.

Das Aufrufen von bindService oder das Aufrufen einer Absicht, die vom Dienst abgefangen werden kann, ist dann keine gute Idee, da der Dienst gestartet wird, wenn er nicht ausgeführt wird.

Wie miracle2k vorgeschlagen hat, ist es am besten, ein statisches Feld in der Serviceklasse zu haben, um zu wissen, ob der Service gestartet wurde oder nicht.

Um es noch sauberer zu machen, schlage ich vor, den Dienst in einen Singleton mit einem sehr sehr faulen Abruf umzuwandeln: Das heißt, es gibt überhaupt keine Instanziierung der Singleton- Instanz durch statische Methoden. Die statische getInstance-Methode Ihres Dienstes / Singletons gibt nur die Instanz des Singletons zurück, wenn dieser erstellt wurde. Der Singleton selbst wird jedoch nicht gestartet oder instanziiert. Der Dienst wird nur über normale Dienststartmethoden gestartet.

Es wäre dann noch sauberer, das Singleton-Entwurfsmuster zu ändern, um die verwirrende getInstance-Methode in so etwas wie die isInstanceCreated() : booleanMethode umzubenennen .

Der Code sieht folgendermaßen aus:

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

Diese Lösung ist elegant, aber nur relevant, wenn Sie Zugriff auf die Serviceklasse haben und nur für Klassen neben der App / dem Paket des Service. Wenn sich Ihre Klassen außerhalb der Service-App / des Service-Pakets befinden, können Sie den ActivityManager mit den von Pieter-Jan Van Robays unterstrichenen Einschränkungen abfragen.

Snicolas
quelle
32
Das ist fehlerhaft. Es wird nicht garantiert, dass onDestroy aufgerufen wird.
Pacerier
8
Wenn das System nicht genügend Arbeitsspeicher hat, wird Ihr Dienst automatisch beendet, ohne dass Ihr onDestroy angerufen wird. Deshalb sage ich, dass dies fehlerhaft ist.
Pacerier
17
@Pacerier, aber wenn das System den Prozess abbricht, wird das Instanzflag immer noch zurückgesetzt. Ich vermute, dass beim statischen Laden des Empfängers (nachdem das System den Dienst beendet hat) das statische Flag 'Instanz' als Null neu erstellt wird.
Tom
2
Zumindest besser als das Durchlaufen all dieser Dienste in isMyServiceRunning, was die Dinge wirklich verzögert, wenn sie bei jeder
Gerätedrehung ausgeführt werden
1
Ihre Instanzvariable sollte nicht als endgültig deklariert werden, da sie sonst von den Methoden onCreate () oder onDestroy () nicht festgelegt oder auf Null gesetzt werden kann.
k2col
27

Sie können dies verwenden (ich habe es noch nicht versucht, aber ich hoffe, dass es funktioniert):

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

Die startService-Methode gibt ein ComponentName-Objekt zurück, wenn bereits ein Dienst ausgeführt wird. Wenn nicht, wird null zurückgegeben.

Siehe public abstract ComponentName startService (Absichtsdienst) .

Dies ist nicht wie eine Überprüfung, denke ich, weil es den Dienst startet, so dass Sie stopService(someIntent);unter dem Code hinzufügen können .

Keenan Gebze
quelle
11
Nicht genau das, was die Dokumente sagen. Laut Ihrem Link: "Rückgabe Wenn der Dienst gestartet wird oder bereits ausgeführt wird, wird der Komponentenname des tatsächlich gestarteten Dienstes zurückgegeben. Andernfalls wird null zurückgegeben, wenn der Dienst nicht vorhanden ist."
Gabriel
Nettes Denken ... passt aber nicht in die aktuelle Situation.
Code_Life
5
Dies ist nicht der richtige Weg, da beim IDE-Trigger dies if(startService(someIntent) != null)überprüft wird, IsserviceRunningaber auch ein neuer Dienst abgespielt wird.
Chintan Khetiya
Wie bereits erwähnt, ist es für dieses Problem hilfreich, wenn Sie den Dienst nach dieser Steuerung beenden. Aber warum einen Dienst umsonst starten und stoppen?
Taner
6
Dadurch wird der Dienst gestartet, nicht wahr? Ich möchte nur den Status des Dienstes überprüfen, anstatt ihn zu starten ...
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;
}
JR
quelle
21

Ein Auszug aus Android- Dokumenten:

Wie sendBroadcast (Intent) , aber wenn es Empfänger für den Intent gibt, blockiert diese Funktion diese und versendet sie sofort, bevor sie zurückkehren.

Stellen Sie sich diesen Hack als "Ping" vorService . Da wir synchron senden können, können wir synchron über den UI-Thread senden und ein Ergebnis erhalten .

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

Der Gewinner in vielen Anwendungen ist natürlich ein statisches boolesches Feld im Dienst, das auf truein Service.onCreate()und auf falsein gesetzt ist, Service.onDestroy()weil es viel einfacher ist.

Peterchaula
quelle
Dies ist eine viel bessere Lösung als die akzeptierte, die fehlschlägt, wenn Android den Dienst beendet, da die globale Variablenmethode immer noch anzeigt, dass der Dienst ausgeführt wird, wenn er tatsächlich nicht mehr ausgeführt wird. Dieser synchrone Ping-Pong-Broadcast-Trick ist tatsächlich die EINZIGE zuverlässige Methode, um zu überprüfen, ob ein Dienst aktiv ist. Sie können den Dienst einfach anfordern, wenn er vorhanden ist. Wenn es antwortet, ist der Dienst aktiv und läuft. Wenn nicht, wurde er entweder nicht gestartet oder entweder programmgesteuert oder vom System heruntergefahren, um Speicher wiederherzustellen.
Phoenix wurde
13

Ich habe eine der oben vorgestellten Lösungen leicht modifiziert, aber die Klasse anstelle eines generischen Zeichenfolgennamens übergeben, um sicherzugehen, dass Zeichenfolgen verglichen werden, die aus derselben Methode stammen 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;
    }
}

und dann

Boolean isServiceRunning = ServiceTools.isServiceRunning(
                    MainActivity.this.getApplicationContext(),
                    BackgroundIntentService.class);
loretoparisi
quelle
Um strenger zu sein, können Sie die Klassenparameter inClass<? extends Service>
silentsudo
11

Der richtige Weg, um zu überprüfen, ob ein Dienst ausgeführt wird, besteht darin, ihn einfach zu fragen. Implementieren Sie einen BroadcastReceiver in Ihrem Dienst, der auf Pings aus Ihren Aktivitäten reagiert. Registrieren Sie den BroadcastReceiver, wenn der Dienst gestartet wird, und heben Sie die Registrierung auf, wenn der Dienst zerstört wird. Senden Sie aus Ihrer Aktivität (oder einer beliebigen Komponente) eine lokale Broadcast- Absicht an den Dienst. Wenn dieser antwortet, wissen Sie, dass er ausgeführt wird. Beachten Sie den subtilen Unterschied zwischen ACTION_PING und ACTION_PONG im folgenden Code.

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;
            }
        }
    };
}
Ben H.
quelle
1
Ich mag diesen Ansatz wirklich. Es ist ein wenig schwer Code, wird aber immer funktionieren. Ich sehe nicht, dass die Absichten der Übertragung bald veraltet sind :)
ShellDude
8

Ich möchte der Antwort von @Snicolas nur eine Notiz hinzufügen. Mit den folgenden Schritten können Sie den Stoppdienst mit / ohne Anruf überprüfen onDestroy().

  1. onDestroy() aufgerufen: Gehen Sie zu Einstellungen -> Anwendung -> Ausführen von Diensten -> Wählen Sie Ihren Dienst aus und beenden Sie ihn.

  2. onDestroy()nicht aufgerufen: Gehen Sie zu Einstellungen -> Anwendung -> Anwendungen verwalten -> Wählen Sie Ihre Anwendung aus, in der Ihr Dienst ausgeführt wird, und klicken Sie auf "Stopp erzwingen". Da Ihre Anwendung hier jedoch gestoppt wird, werden definitiv auch die Dienstinstanzen gestoppt.

Abschließend möchte ich erwähnen, dass der dort erwähnte Ansatz, bei dem eine statische Variable in der Singleton-Klasse verwendet wird, für mich funktioniert.

Paul
quelle
7

onDestroy wird nicht immer im Service angerufen, das ist also nutzlos!

Beispiel: Führen Sie die App mit einer Änderung von Eclipse erneut aus. Die Anwendung wird mit SIG: 9 zwangsweise beendet.

Kevin Parker
quelle
6

Zunächst sollten Sie nicht versuchen, den Dienst mit dem ActivityManager zu erreichen. (Diskutiert hier )

Dienste können eigenständig ausgeführt werden, an eine Aktivität gebunden sein oder beides. Sie können eine Aktivität einchecken, wenn Ihr Dienst ausgeführt wird oder nicht, indem Sie eine Schnittstelle (die Binder erweitert) erstellen, in der Sie Methoden deklarieren, die sowohl die Aktivität als auch der Dienst verstehen. Sie können dies tun, indem Sie eine eigene Schnittstelle erstellen, in der Sie beispielsweise "isServiceRunning ()" deklarieren. Sie können dann Ihre Aktivität an Ihren Dienst binden, die Methode isServiceRunning () ausführen, der Dienst prüft selbst, ob sie ausgeführt wird oder nicht, und gibt einen Booleschen Wert an Ihre Aktivität zurück.

Sie können diese Methode auch verwenden, um Ihren Dienst zu beenden oder auf andere Weise mit ihm zu interagieren.

In diesem Tutorial habe ich gelernt, wie dieses Szenario in meiner Anwendung implementiert wird.

Pieter-Jan Van Robays
quelle
3
Diese Diskussion fand am 26.12.07 statt. Entweder ist das Juli dieses Jahres (dh in der Zukunft) oder bevor Android überhaupt öffentlich war. So oder so, das lässt mich es nicht vertrauen.
Tom
Diese Diskussion findet am 26. Dezember 2007 statt. Sie diskutieren eine Vorabversion, die meiner Meinung nach ( developer.android.com/sdk/OLD_RELEASENOTES.html#m3-rc37a ) am 14. Dezember 2007 veröffentlicht wurde.
ingh.am
6

Wieder eine andere Alternative, die Menschen möglicherweise sauberer finden, wenn sie ausstehende Absichten verwenden (zum Beispiel mit 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);
}

Wo CODEist eine Konstante, die Sie privat in Ihrer Klasse definieren, um die ausstehenden Absichten zu identifizieren, die Ihrem Service zugeordnet sind.

Snicolas
quelle
1
Kombinieren oder aktualisieren Sie Ihre vorherige Antwort. Bitte veröffentlichen Sie nicht mehr als eine Antwort pro Beitrag.
ChuongPham
Kann diese Antwort erweitert werden, dh wie ordnet man den Wert für CODE dem Service zu?
Dave Nottage
Woher bekomme ich Kontext?
Basilikum
6

Unten ist ein eleganter Hack, der alle abdeckt Ifs. Dies gilt nur für lokale Dienste.

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

Und später:

    if(AService.isServiceCreated()){
        ...
    }else{
        startService(...);
    }
TheRealChx101
quelle
Das einzige Problem dabei ist, wenn der Dienst ein Sticky-Dienst ist und sich selbst neu startet. Der Aufruf von isServiceCreated () gibt nach dem erneuten Start des Dienstes false zurück, da mInstance null ist.
Mira_Cole
1
Würde onCreate dann nicht aufgerufen, wenn sich der Dienst selbst neu startet?
TheRealChx101
6

Xamarin C # -Version:

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;
}
Nima
quelle
Sie benötigen den "Kontext" für GetSystemService.
Testen
5

Für den hier angegebenen Anwendungsfall können wir einfach stopService()den Rückgabewert der Methode verwenden. Es wird zurückgegeben, truewenn der angegebene Dienst vorhanden ist und beendet wird. Sonst kehrt es zurück false. Sie können den Dienst also neu starten, wenn das Ergebnis falseanders ist. Es wird sichergestellt, dass der aktuelle Dienst gestoppt wurde. :) Es wäre besser, wenn Sie einen Blick auf haben diese .

Rahul Raveendran
quelle
5

Ein anderer Ansatz mit Kotlin. Inspiriert von den Antworten anderer Benutzer

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

Als Kotlin-Erweiterung

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

Verwendungszweck

context.isMyServiceRunning(MyService::class.java)
Oscar Emilio Perez Martinez
quelle
4

In kotlin können Sie eine boolesche Variable in das Begleitobjekt einfügen und deren Wert aus einer beliebigen Klasse überprüfen:

companion object{
     var isRuning = false

}

Ändern Sie den Wert, wenn der Dienst erstellt und zerstört wird

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

override fun onDestroy() {
    super.onDestroy()
    isRuning = false
    }
Mohamed Sabre
quelle
3

Verwenden Sie in Ihrer Service-Unterklasse einen statischen Booleschen Wert, um den Status des Service abzurufen, wie unten gezeigt.

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)
        }
    }
}
EdgeDev
quelle
3

Für Kotlin können Sie den folgenden Code verwenden.

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
}
PA Gosai
quelle
Dies ist eine großartige Antwort zum Schreiben von Tests, da Sie sie verwenden können, ohne Ihren Arbeitscode zu ändern.
Robert Liberatore
2

Die Antwort von geekQ aber in der Kotlin-Klasse. Danke 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
}

Der Anruf

isMyServiceRunning(NewService::class.java)
Luis Eduardo Moreno
quelle
6
ActivityManager.getRunningServicesist seit Android O
Daniel Shatz
1

Es kann mehrere Dienste mit demselben Klassennamen geben.

Ich habe gerade zwei Apps erstellt. Der Paketname der ersten App lautet com.example.mock. Ich habe ein Unterpaket erstellt, das loremin der App aufgerufen wurde, und einen Dienst namens Mock2Service. Sein voll qualifizierter Name ist also com.example.mock.lorem.Mock2Service.

Dann habe ich die zweite App und einen Dienst namens erstellt Mock2Service. Der Paketname der zweiten App lautet com.example.mock.lorem. Der vollqualifizierte Name des Dienstes ist com.example.mock.lorem.Mock2Serviceebenfalls.

Hier ist meine Logcat-Ausgabe.

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

Eine bessere Idee ist zu vergleichen ComponentNameFällen wegen equals()der ComponentNamebeiden Paketnamen und Klassennamen vergleicht. Auf einem Gerät können nicht zwei Apps mit demselben Paketnamen installiert sein.

Die Methode equals () von 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;
}

Komponentenname

Maksim Dmitriev
quelle
1

Bitte benutzen Sie diesen 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;
    }
Nikunj Sorathiya
quelle
0

Dies gilt eher für das Debuggen von Intent-Diensten, da sie einen Thread erzeugen, aber möglicherweise auch für reguläre Dienste funktionieren. Ich habe diesen Thread dank Binging gefunden

In meinem Fall habe ich mit dem Debugger herumgespielt und die Thread-Ansicht gefunden. Es sieht aus wie das Aufzählungszeichen in MS Word. Wie auch immer, Sie müssen sich nicht im Debugger-Modus befinden, um es zu verwenden. Klicken Sie auf den Prozess und dann auf diese Schaltfläche. Alle Intent Services werden angezeigt, während sie ausgeführt werden, zumindest auf dem Emulator.

Joe Plante
quelle
0

Wenn der Dienst zu einem anderen Prozess oder APK gehört, verwenden Sie die auf dem ActivityManager basierende Lösung.

Wenn Sie Zugriff auf die Quelle haben, verwenden Sie einfach die Lösung basierend auf einem statischen Feld. Stattdessen würde ich die Verwendung eines Booleschen Werts vorschlagen. Während der Dienst ausgeführt wird, aktualisieren Sie einfach seinen Wert auf "Jetzt" und setzen Sie ihn nach Abschluss auf "Null". Anhand der Aktivität können Sie überprüfen, ob sie null ist oder das Datum zu alt ist, was bedeutet, dass sie nicht ausgeführt wird.

Sie können auch eine Broadcast-Benachrichtigung von Ihrem Dienst senden, die angibt, dass weitere Informationen wie der Fortschritt ausgeführt werden.

FranMowinckel
quelle
0

In TheServiceClass definieren Sie:

 public static Boolean serviceRunning = false;

Dann In onStartCommand (...)

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

    serviceRunning = true;
    ...
}

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

} 

Rufen Sie dann if(TheServiceClass.serviceRunning == true)aus einer beliebigen Klasse an.

Badr
quelle
4
Dies funktioniert nicht, wenn Ihr Dienst von Android beendet wird.
Heisenberg
@ Heisenberg Das habe ich gerade selbst erlebt. Weißt du warum nicht?
Tim
@ Heisenberg, wenn meine App vom Betriebssystem beendet wird, startet der Dienst neu und setzt den statischen Bool auf true, aber nach dem Erhalt meldet er false
Tim
Dies funktioniert nicht, wenn Sie anrufen stopService. Zumindest für Intent-Services. onDestroy()wird sofort angerufen, onHandleIntent()läuft aber noch
serggl
1
@ Heisenberg Wird das Beenden des Dienstes aufgrund des geringen Speichers nicht auch das Beenden des Prozesses bedeuten?
Android-Entwickler
0

einfache Verwendung binden mit nicht automatisch erstellen - siehe ps. und aktualisieren ...

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

Beispiel:

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

warum nicht verwenden? getRunningServices ()

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

Hinweis: Diese Methode ist nur zum Debuggen oder Implementieren von Benutzeroberflächen vom Typ Service Management vorgesehen.


ps. Android-Dokumentation ist irreführend Ich habe ein Problem auf Google Tracker geöffnet, um alle Zweifel zu beseitigen:

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

Wie wir sehen können, ruft der Bindedienst tatsächlich eine Transaktion über den ActivityManager-Ordner über die Service-Cache-Ordner auf. Ich verfolge nicht, welcher Dienst für die Bindung verantwortlich ist, aber wie wir sehen können, lautet das Ergebnis für die Bindung:

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

Transaktion erfolgt über Ordner:

ServiceManager.getService("activity");

Nächster:

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

Dies wird in ActivityThread festgelegt über:

 public final void bindApplication(...) {

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

Dies wird in ActivityManagerService in der Methode aufgerufen:

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

dann:

 private HashMap<String, IBinder> getCommonServicesLocked() {

aber es gibt keine "Aktivität" nur Fensterpaket und Alarm ..

Also müssen wir zurückrufen, um anzurufen:

 return getIServiceManager().getService(name);

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

Dies macht Anruf durch:

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

was dazu führt :

BinderInternal.getContextObject()

und das ist native Methode ....

  /**
     * 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();

Ich habe jetzt keine Zeit, mich in c zu vertiefen. Bis ich den Restanruf seziere, setze ich meine Antwort aus.

Der beste Weg, um zu überprüfen, ob der Dienst ausgeführt wird, besteht darin, eine Bindung zu erstellen (wenn die Bindung nicht erstellt wurde, existiert kein Dienst) - und den Dienst über die Bindung nach seinem Status abzufragen (unter Verwendung des gespeicherten internen Flags für den Status).

Update 23.06.2018

Ich fand die interessant:

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

Zusamenfassend :)

"Stellen Sie einen Ordner für einen bereits gebundenen Dienst bereit. Diese Methode ist synchron und startet den Zieldienst nicht, wenn er nicht vorhanden ist."

public IBinder peekService (Intent Service, String ResolutionedType, String Calling Package) löst eine RemoteException aus.

* *

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

* *

ceph3us
quelle
bindResult (Rückgabewert der bindService-Methode) wird nicht als falsch angegeben, wenn der Dienst nicht ausgeführt wird.
Shangeeth Sivan
0

Meine Kotlin-Konvertierung der ActivityManager::getRunningServicesbasierten Antworten. Fügen Sie diese Funktion in eine Aktivität ein.

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

Sie können diese Optionen in den Android Developer-Optionen verwenden, um festzustellen, ob Ihr Dienst noch im Hintergrund ausgeführt wird.

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.
Haomin
quelle
-5

Nimm es ruhig Jungs ... :)

Ich denke, die am besten geeignete Lösung besteht darin, ein Schlüssel-Wert-Paar SharedPreferenceszu halten, ob der Dienst ausgeführt wird oder nicht.

Die Logik ist sehr klar; an jeder gewünschten Position in Ihrer Serviceklasse; Setzen Sie einen booleschen Wert, der als Flag für Sie dient, ob der Dienst ausgeführt wird oder nicht. Lesen Sie diesen Wert dann an einer beliebigen Stelle in Ihrer Anwendung.

Ein Beispielcode, den ich in meiner App verwende, ist unten:

In meiner Service-Klasse (Ein Service für Audio Stream) führe ich den folgenden Code aus, wenn der Service aktiv ist.

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

In jeder Aktivität meiner Anwendung überprüfe ich dann den Status des Dienstes mithilfe des folgenden Codes.

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

Keine besonderen Berechtigungen, keine Schleifen ... Einfacher Weg, saubere Lösung :)

Wenn Sie zusätzliche Informationen benötigen, klicken Sie bitte auf den Link

Hoffe das hilft.

Taner
quelle
19
Nur dass niemand den Wert für Sie aktualisiert, wenn er den Dienst beendet
Gunnar Forsgren - Mobimation
Wenn der Dienst beendet wird, wird onDestroy () ausgelöst und es ist möglich, seinen Status zu aktualisieren
Jongz Puangput
5
@JongzPuangput, onDestroywird nicht immer aufgerufen, wenn der Dienst beendet wird. Zum Beispiel habe ich gesehen, dass meine Dienste in Situationen mit wenig Arbeitsspeicher getötet wurden, ohne onDestroyangerufen zu werden.
Sam
@ Sam Wie wird dann gerufen?
Ruchir Baronia
2
@RuchirBaronia Soweit ich mich erinnere, werden Sie einfach nicht benachrichtigt, wenn Ihre Sachen getötet werden. Ich glaube, Android wurde entwickelt, um Apps nach Bedarf zu töten, und Apps sollten so konzipiert sein, dass sie zu jedem Zeitpunkt ohne Benachrichtigung getötet werden können.
Sam