Überblick über die Dienste

Ein Service ist eine Anwendungskomponente, die lang laufende Vorgänge im Hintergrund ausführen kann. Es bietet keine Benutzeroberfläche. Nach dem Start kann ein Dienst noch einige Zeit lang ausgeführt werden, auch wenn der Nutzer zu einer anderen Anwendung wechselt. Außerdem kann eine Komponente an einen Dienst gebunden werden, um mit ihm zu interagieren und sogar eine interprozedurale Kommunikation (IPC) durchzuführen. Ein Dienst kann beispielsweise Netzwerktransaktionen verarbeiten, Musik abspielen, Datei-I/O ausführen oder mit einem Inhaltsanbieter interagieren – und das alles im Hintergrund.

Achtung:Ein Dienst wird im Hauptthread seines Hostingprozesses ausgeführt. Er erstellt keinen eigenen Thread und wird nicht in einem separaten Prozess ausgeführt, sofern Sie nichts anderes angeben. Sie sollten alle blockierenden Vorgänge in einem separaten Thread innerhalb des Dienstes ausführen, um „App antwortet nicht“-Fehler (ANRs) zu vermeiden.

Arten von Diensten

Es gibt drei verschiedene Arten von Diensten:

Vordergrund

Ein Dienst im Vordergrund führt einen Vorgang aus, der für den Nutzer wahrnehmbar ist. Eine Audio-App würde beispielsweise einen Dienst im Vordergrund verwenden, um einen Audiotrack abzuspielen. Für Dienste im Vordergrund muss eine Benachrichtigung angezeigt werden. Dienste im Vordergrund werden auch dann ausgeführt, wenn der Nutzer nicht mit der App interagiert.

Wenn Sie einen Dienst im Vordergrund verwenden, müssen Sie eine Benachrichtigung anzeigen, damit Nutzer aktiv darüber informiert werden, dass der Dienst ausgeführt wird. Diese Benachrichtigung kann nur geschlossen werden, wenn der Dienst angehalten oder aus dem Vordergrund entfernt wird.

Weitere Informationen zum Konfigurieren von Diensten im Vordergrund in Ihrer App

Hinweis:Die WorkManager API bietet eine flexible Möglichkeit zum Planen von Aufgaben und kann diese Jobs bei Bedarf als Dienste im Vordergrund ausführen. In vielen Fällen ist die Verwendung von WorkManager vorzuziehen, anstatt Dienste im Vordergrund direkt zu verwenden.

Hintergrund
Ein Hintergrunddienst führt eine Aktion aus, die vom Nutzer nicht direkt wahrgenommen wird. Wenn eine App beispielsweise einen Dienst zum Komprimieren des Speichers verwendet, handelt es sich in der Regel um einen Hintergrunddienst.

Hinweis:Wenn Ihre App auf API-Level 26 oder höher ausgerichtet ist, schränkt das System die Ausführung von Hintergrunddiensten ein, wenn sich die App nicht im Vordergrund befindet. In den meisten Fällen sollten Sie beispielsweise nicht im Hintergrund auf Standortinformationen zugreifen. Verwenden Sie stattdessen WorkManager, um Aufgaben zu planen.

Gekoppelt
Ein Dienst ist gebunden, wenn eine Anwendungskomponente ihn durch Aufrufen von bindService() bindet. Ein gebundener Dienst bietet eine Client-Server-Schnittstelle, über die Komponenten mit dem Dienst interagieren, Anfragen senden, Ergebnisse empfangen und dies sogar über Prozesse hinweg mit Inter-Process-Communication (IPC) tun können. Ein verknüpfter Dienst wird nur ausgeführt, solange eine andere Anwendungskomponente damit verknüpft ist. Mehrere Komponenten können gleichzeitig an den Dienst gebunden werden. Wenn sie alle getrennt werden, wird der Dienst zerstört.

In dieser Dokumentation werden gestartete und gebundene Dienste zwar im Allgemeinen getrennt behandelt, Ihr Dienst kann aber beides sein: Er kann gestartet werden (um unbegrenzt zu laufen) und auch eine Bindung zulassen. Es geht einfach darum, ob Sie ein paar Callback-Methoden implementieren: onStartCommand(), um Komponenten zu ermöglichen, sie zu starten, und onBind(), um die Bindung zu ermöglichen.

Unabhängig davon, ob Ihr Dienst gestartet, gebunden oder beides ist, kann jede Anwendungskomponente den Dienst (auch von einer separaten Anwendung aus) auf die gleiche Weise verwenden, wie eine Komponente eine Aktivität verwenden kann – indem sie mit einer Intent gestartet wird. Sie können den Dienst jedoch in der Manifestdatei als privat deklarieren und den Zugriff von anderen Anwendungen blockieren. Weitere Informationen finden Sie im Abschnitt Dienst im Manifest deklarieren.

Zwischen einem Dienst und einem Thread wählen

Ein Dienst ist einfach eine Komponente, die auch dann im Hintergrund ausgeführt werden kann, wenn der Nutzer nicht mit Ihrer Anwendung interagiert. Sie sollten also nur dann einen Dienst erstellen, wenn Sie ihn benötigen.

Wenn Sie Aufgaben außerhalb Ihres Hauptthreads ausführen müssen, aber nur, während der Nutzer mit Ihrer Anwendung interagiert, sollten Sie stattdessen einen neuen Thread im Kontext einer anderen Anwendungskomponente erstellen. Wenn Sie beispielsweise Musik abspielen möchten, aber nur, während Ihre Aktivität ausgeführt wird, können Sie einen Thread in onCreate() erstellen, in onStart() starten und in onStop() beenden. Sie können auch Threadpools und Executors aus dem java.util.concurrent-Paket oder Kotlin-Coroutinen anstelle der traditionellen Thread-Klasse verwenden. Weitere Informationen zum Verschieben der Ausführung in Hintergrundthreads finden Sie im Dokument Threading unter Android.

Denken Sie daran, dass ein Dienst, den Sie verwenden, standardmäßig im Hauptthread Ihrer Anwendung ausgeführt wird. Sie sollten also trotzdem einen neuen Thread innerhalb des Dienstes erstellen, wenn er intensive oder blockierende Vorgänge ausführt.

Grundlagen

Wenn Sie einen Dienst erstellen möchten, müssen Sie eine Unterklasse von Service erstellen oder eine der vorhandenen Unterklassen verwenden. In Ihrer Implementierung müssen Sie einige Rückrufmethoden überschreiben, die wichtige Aspekte des Dienstlebenszyklus verarbeiten, und einen Mechanismus bereitstellen, mit dem sich die Komponenten gegebenenfalls an den Dienst binden lassen. Das sind die wichtigsten Callback-Methoden, die Sie überschreiben sollten:

onStartCommand()
Das System ruft diese Methode auf, indem es startService() aufruft, wenn eine andere Komponente (z. B. eine Aktivität) den Start des Dienstes anfordert. Wenn diese Methode ausgeführt wird, wird der Dienst gestartet und kann unbegrenzt im Hintergrund ausgeführt werden. Wenn Sie diese Funktion implementieren, sind Sie dafür verantwortlich, den Dienst zu beenden, wenn die Arbeit abgeschlossen ist, indem Sie stopSelf() oder stopService() aufrufen. Wenn Sie nur eine Bindung bereitstellen möchten, müssen Sie diese Methode nicht implementieren.
onBind()
Das System ruft diese Methode durch Aufrufen von bindService() auf, wenn eine andere Komponente eine Bindung zum Dienst herstellen möchte (z. B. um einen RPC auszuführen). Bei der Implementierung dieser Methode müssen Sie eine Schnittstelle bereitstellen, über die Clients mit dem Dienst kommunizieren, indem sie einen IBinder zurückgeben. Sie müssen diese Methode immer implementieren. Wenn Sie die Bindung jedoch nicht zulassen möchten, sollten Sie null zurückgeben.
onCreate()
Das System ruft diese Methode auf, um einmalige Einrichtungsverfahren auszuführen, wenn der Dienst zum ersten Mal erstellt wird (bevor onStartCommand() oder onBind() aufgerufen wird). Wenn der Dienst bereits ausgeführt wird, wird diese Methode nicht aufgerufen.
onDestroy()
Das System ruft diese Methode auf, wenn der Dienst nicht mehr verwendet wird und gelöscht wird. Ihr Dienst sollte dies implementieren, um alle Ressourcen wie Threads, registrierte Listener oder Empfänger zu bereinigen. Dies ist der letzte Aufruf, den der Dienst empfängt.

Wenn eine Komponente den Dienst durch Aufrufen von startService() startet (was zu einem Aufruf von onStartCommand() führt), läuft der Dienst so lange weiter, bis er sich selbst mit stopSelf() beendet oder eine andere Komponente ihn durch Aufrufen von stopService() beendet.

Wenn eine Komponente bindService() aufruft, um den Dienst zu erstellen, und onStartCommand() nicht aufgerufen wird, wird der Dienst nur so lange ausgeführt, wie die Komponente daran gebunden ist. Nachdem der Dienst von allen seinen Clients getrennt wurde, wird er vom System gelöscht.

Das Android-System beendet einen Dienst nur, wenn der Arbeitsspeicher knapp ist und Systemressourcen für die Aktivität wiederhergestellt werden müssen, die sich im Fokus des Nutzers befindet. Wenn der Dienst an eine Aktivität gebunden ist, die sich im Fokus des Nutzers befindet, ist die Wahrscheinlichkeit geringer, dass er beendet wird. Wenn der Dienst im Vordergrund ausgeführt wird, wird er selten beendet. Wenn der Dienst gestartet ist und lange läuft, wird seine Position in der Liste der Hintergrundaufgaben im Laufe der Zeit herabgestuft und der Dienst ist sehr anfällig für das Beenden. Wenn Ihr Dienst gestartet wird, muss er so konzipiert sein, dass er Systemneustarts problemlos verarbeiten kann. Wenn das System Ihren Dienst beendet, wird er neu gestartet, sobald Ressourcen verfügbar sind. Dies hängt jedoch auch vom Wert ab, den Sie von onStartCommand() zurückgeben. Weitere Informationen dazu, wann das System einen Dienst zerstört, finden Sie im Dokument Prozesse und Threads.

In den folgenden Abschnitten erfahren Sie, wie Sie die Dienstmethoden startService() und bindService() erstellen und wie Sie sie in anderen Anwendungskomponenten verwenden.

Dienst im Manifest deklarieren

Sie müssen alle Dienste in der Manifestdatei Ihrer Anwendung deklarieren, genau wie bei Aktivitäten und anderen Komponenten.

Fügen Sie zum Deklarieren Ihres Dienstes ein <service>-Element als untergeordnetes Element des <application>-Elements hinzu. Hier ein Beispiel:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

Weitere Informationen zum Deklarieren Ihres Dienstes im Manifest finden Sie in der Elementreferenz für <service>.

Es gibt noch weitere Attribute, die Sie im Element <service> angeben können, um Eigenschaften wie die Berechtigungen zum Starten des Dienstes und den Prozess zu definieren, in dem der Dienst ausgeführt werden soll. Das Attribut android:name ist das einzige erforderliche Attribut. Es gibt den Klassennamen des Dienstes an. Lassen Sie diesen Namen nach der Veröffentlichung Ihrer Anwendung unverändert, um das Risiko von Codeausfällen zu vermeiden, die durch die Abhängigkeit von expliziten Intents zum Starten oder Binden des Dienstes entstehen können (siehe Blogpost Dinge, die sich nicht ändern können).

Achtung: Damit Ihre App sicher ist, sollten Sie beim Starten einer Service immer eine explizite Intent verwenden und keine Intent-Filter für Ihre Dienste deklarieren. Die Verwendung einer impliziten Absicht zum Starten eines Dienstes stellt ein Sicherheitsrisiko dar, da Sie nicht sicher sein können, welcher Dienst auf die Absicht reagiert, und der Nutzer nicht sehen kann, welcher Dienst gestartet wird. Ab Android 5.0 (API-Level 21) wirft das System eine Ausnahme, wenn Sie bindService() mit einer impliziten Intent aufrufen.

Sie können dafür sorgen, dass Ihr Dienst nur für Ihre App verfügbar ist, indem Sie das Attribut android:exported einfügen und auf false festlegen. Dadurch wird verhindert, dass andere Apps Ihren Dienst starten, auch wenn ein expliziter Intent verwendet wird.

Hinweis: Nutzer können sehen, welche Dienste auf ihrem Gerät ausgeführt werden. Wenn er einen Dienst sieht, den er nicht kennt oder dem er nicht vertraut, kann er ihn beenden. Damit Ihr Dienst nicht versehentlich von Nutzern beendet wird, müssen Sie dem Element <service> im App-Manifest das Attribut android:description hinzufügen. Geben Sie in der Beschreibung in einem kurzen Satz an, was der Dienst tut und welche Vorteile er bietet.

Gestarteten Dienst erstellen

Ein gestarteter Dienst wird von einer anderen Komponente gestartet, indem startService() aufgerufen wird, was zu einem Aufruf der onStartCommand()-Methode des Dienstes führt.

Wenn ein Dienst gestartet wird, hat er einen Lebenszyklus, der unabhängig von der Komponente ist, die ihn gestartet hat. Der Dienst kann unbegrenzt im Hintergrund ausgeführt werden, auch wenn die Komponente, die ihn gestartet hat, zerstört wird. Daher sollte der Dienst sich selbst beenden, wenn seine Aufgabe abgeschlossen ist, indem er stopSelf() aufruft. Eine andere Komponente kann ihn auch beenden, indem sie stopService() aufruft.

Eine Anwendungskomponente wie eine Aktivität kann den Dienst starten, indem sie startService() aufruft und einen Intent übergibt, der den Dienst angibt und alle Daten enthält, die vom Dienst verwendet werden sollen. Der Dienst empfängt diese Intent in der onStartCommand()-Methode.

Angenommen, für eine Aktivität müssen einige Daten in einer Onlinedatenbank gespeichert werden. Die Aktivität kann einen Companion-Dienst starten und ihm die zu speichernden Daten übergeben, indem eine Intent an startService() übergeben wird. Der Dienst empfängt die Absicht in onStartCommand(), stellt eine Verbindung zum Internet her und führt die Datenbanktransaktion aus. Nach Abschluss der Transaktion wird der Dienst beendet und gelöscht.

Achtung:Ein Dienst wird standardmäßig im selben Prozess wie die Anwendung ausgeführt, in der er deklariert ist, und im Hauptthread dieser Anwendung. Wenn Ihr Dienst intensive oder blockierende Vorgänge ausführt, während der Nutzer mit einer Aktivität in derselben Anwendung interagiert, wird die Aktivitätsleistung durch den Dienst verlangsamt. Um Auswirkungen auf die Anwendungsleistung zu vermeiden, starten Sie einen neuen Thread im Dienst.

Die Klasse Service ist die Basisklasse für alle Dienste. Wenn Sie diese Klasse erweitern, ist es wichtig, einen neuen Thread zu erstellen, in dem der Dienst alle seine Aufgaben erledigen kann. Der Dienst verwendet standardmäßig den Hauptthread Ihrer Anwendung, was die Leistung aller Aktivitäten verlangsamen kann, die in Ihrer Anwendung ausgeführt werden.

Das Android-Framework bietet auch die Unterklasse IntentService von Service, die einen Worker-Thread verwendet, um alle Startanfragen einzeln zu verarbeiten. Die Verwendung dieser Klasse wird für neue Apps nicht empfohlen, da sie ab Android 8 Oreo aufgrund der Einführung von Limits für die Ausführung im Hintergrund nicht mehr richtig funktioniert. Außerdem wird sie ab Android 11 eingestellt. Sie können JobIntentService als Ersatz für IntentService verwenden, der mit neueren Android-Versionen kompatibel ist.

In den folgenden Abschnitten wird beschrieben, wie Sie Ihren eigenen benutzerdefinierten Dienst implementieren können. Für die meisten Anwendungsfälle sollten Sie jedoch stattdessen WorkManager verwenden. Im Leitfaden zur Hintergrundverarbeitung unter Android finden Sie Informationen dazu, ob es eine Lösung gibt, die Ihren Anforderungen entspricht.

Dienstklasse erweitern

Sie können die Service-Klasse erweitern, um jeden eingehenden Intent zu verarbeiten. So könnte eine einfache Implementierung aussehen:

Kotlin

class HelloService : Service() {

    private var serviceLooper: Looper? = null
    private var serviceHandler: ServiceHandler? = null

    // Handler that receives messages from the thread
    private inner class ServiceHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5000)
            } catch (e: InterruptedException) {
                // Restore interrupt status.
                Thread.currentThread().interrupt()
            }

            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            stopSelf(msg.arg1)
        }
    }

    override fun onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
            start()

            // Get the HandlerThread's Looper and use it for our Handler
            serviceLooper = looper
            serviceHandler = ServiceHandler(looper)
        }
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        serviceHandler?.obtainMessage()?.also { msg ->
            msg.arg1 = startId
            serviceHandler?.sendMessage(msg)
        }

        // If we get killed, after returning from here, restart
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        // We don't provide binding, so return null
        return null
    }

    override fun onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
    }
}

Java

public class HelloService extends Service {
  private Looper serviceLooper;
  private ServiceHandler serviceHandler;

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              // Restore interrupt status.
              Thread.currentThread().interrupt();
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service. Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block. We also make it
    // background priority so CPU-intensive work doesn't disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    serviceLooper = thread.getLooper();
    serviceHandler = new ServiceHandler(serviceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = serviceHandler.obtainMessage();
      msg.arg1 = startId;
      serviceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

Der Beispielcode verarbeitet alle eingehenden Aufrufe in onStartCommand() und sendet die Arbeit an einen Handler, der in einem Hintergrund-Thread ausgeführt wird. Er funktioniert genau wie ein IntentService und verarbeitet alle Anfragen nacheinander. Sie können den Code so ändern, dass die Arbeit in einem Thread-Pool ausgeführt wird, z. B. wenn Sie mehrere Anfragen gleichzeitig ausführen möchten.

Die Methode onStartCommand() muss einen Ganzzahlwert zurückgeben. Die Ganzzahl ist ein Wert, der beschreibt, wie der Dienst fortgesetzt werden soll, falls das System ihn beendet. Der Rückgabewert von onStartCommand() muss eine der folgenden Konstanten sein:

START_NOT_STICKY
Wenn das System den Dienst nach der Rückgabe von onStartCommand() beendet, erstelle den Dienst nur dann neu, wenn es ausstehende Übermittlungsabsichten gibt. Dies ist die sicherste Option, um zu vermeiden, dass Ihr Dienst unnötig ausgeführt wird, und um zu ermöglichen, dass Ihre Anwendung alle nicht abgeschlossenen Jobs einfach neu starten kann.
START_STICKY
Wenn das System den Dienst nach der Rückgabe von onStartCommand() beendet, erstellen Sie den Dienst neu und rufen Sie onStartCommand() auf, liefern Sie die letzte Absicht aber nicht noch einmal. Stattdessen ruft das System onStartCommand() mit einer Null-Intention auf, es sei denn, es gibt ausstehende Intents, um den Dienst zu starten. In diesem Fall werden diese Intents gesendet. Dies eignet sich für Mediaplayer (oder ähnliche Dienste), die keine Befehle ausführen, aber unbegrenzt laufen und auf einen Job warten.
START_REDELIVER_INTENT
Wenn das System den Dienst beendet, nachdem onStartCommand() zurückgegeben wurde, erstellen Sie den Dienst neu und rufen Sie onStartCommand() mit der letzten Absicht auf, die an den Dienst gesendet wurde. Alle ausstehenden Intents werden nacheinander gesendet. Dies eignet sich für Dienste, die einen Job aktiv ausführen, der sofort fortgesetzt werden soll, z. B. den Download einer Datei.

Weitere Informationen zu diesen Rückgabewerten finden Sie in der verlinkten Referenzdokumentation für jede Konstante.

Dienst starten

Sie können einen Dienst von einer Aktivität oder einer anderen Anwendungskomponente aus starten, indem Sie Intent an startService() oder startForegroundService() übergeben. Das Android-System ruft die onStartCommand()-Methode des Dienstes auf und übergibt ihr die Intent, die angibt, welcher Dienst gestartet werden soll.

Hinweis: Wenn Ihre App API-Level 26 oder höher anstrebt, schränkt das System die Verwendung oder Erstellung von Hintergrunddiensten ein, es sei denn, die App selbst ist im Vordergrund. Wenn eine App einen Dienst im Vordergrund erstellen muss, sollte sie startForegroundService() aufrufen. Mit dieser Methode wird ein Hintergrunddienst erstellt, aber die Methode signalisiert dem System, dass der Dienst sich selbst in den Vordergrund stellt. Nachdem der Dienst erstellt wurde, muss er innerhalb von fünf Sekunden seine startForeground()-Methode aufrufen.

So kann eine Aktivität beispielsweise den Beispieldienst im vorherigen Abschnitt (HelloService) mit einem expliziten Intent mit startService() starten, wie hier gezeigt:

Kotlin

startService(Intent(this, HelloService::class.java))

Java

startService(new Intent(this, HelloService.class));

Die Methode startService() gibt sofort eine Rückgabe zurück und das Android-System ruft die Methode onStartCommand() des Dienstes auf. Wenn der Dienst noch nicht ausgeführt wird, ruft das System zuerst onCreate() und dann onStartCommand() auf.

Wenn der Dienst keine Bindung bereitstellt, ist die mit startService() übermittelte Absicht der einzige Kommunikationsmodus zwischen der Anwendungskomponente und dem Dienst. Wenn der Dienst jedoch ein Ergebnis zurücksenden soll, kann der Client, der den Dienst startet, ein PendingIntent für eine Übertragung (mit getBroadcast()) erstellen und es im Intent an den Dienst senden, der den Dienst startet. Der Dienst kann dann die Übertragung verwenden, um ein Ergebnis zu liefern.

Mehrere Anfragen zum Starten des Dienstes führen zu mehreren entsprechenden Aufrufen der onStartCommand() des Dienstes. Es ist jedoch nur eine Anfrage zum Beenden des Dienstes (mit stopSelf() oder stopService()) erforderlich.

Dienst beenden

Ein gestarteter Dienst muss seinen eigenen Lebenszyklus verwalten. Das System beendet oder zerstört den Dienst also nur, wenn es den Systemspeicher wiederherstellen muss. Der Dienst wird nach der Rückgabe von onStartCommand() fortgesetzt. Der Dienst muss sich selbst durch Aufrufen von stopSelf() beenden oder eine andere Komponente kann ihn durch Aufrufen von stopService() beenden.

Sobald der Dienst mit stopSelf() oder stopService() beendet wurde, wird er vom System so schnell wie möglich gelöscht.

Wenn Ihr Dienst mehrere Anfragen an onStartCommand() gleichzeitig verarbeitet, sollten Sie den Dienst nicht beenden, wenn Sie mit der Verarbeitung einer Startanfrage fertig sind, da Sie möglicherweise eine neue Startanfrage erhalten haben. Wenn Sie den Dienst am Ende der ersten Anfrage beenden, wird die zweite beendet. Um dieses Problem zu vermeiden, können Sie stopSelf(int) verwenden, damit Ihre Anfrage zum Beenden des Dienstes immer auf der letzten Startanfrage basiert. Wenn Sie stopSelf(int) aufrufen, geben Sie also die ID der Startanfrage (die startId, die an onStartCommand() gesendet wurde) weiter, der Ihre Stoppanfrage entspricht. Wenn der Dienst dann eine neue Startanfrage erhält, bevor Sie stopSelf(int) aufrufen können, stimmt die ID nicht überein und der Dienst wird nicht beendet.

Achtung:Damit keine Systemressourcen verschwendet und die Akkulaufzeit nicht unnötig verkürzt wird, sollten Sie dafür sorgen, dass Ihre Anwendung ihre Dienste beendet, wenn sie fertig ist. Bei Bedarf können andere Komponenten den Dienst beenden, indem sie stopService() aufrufen. Auch wenn Sie die Bindung für den Dienst aktivieren, müssen Sie ihn immer selbst beenden, wenn er einen Aufruf an onStartCommand() erhält.

Weitere Informationen zum Lebenszyklus eines Dienstes finden Sie unten im Abschnitt Lebenszyklus eines Dienstes verwalten.

Verbundenen Dienst erstellen

Ein gebundener Dienst ermöglicht es Anwendungskomponenten, sich durch Aufrufen von bindService() an ihn zu binden, um eine dauerhafte Verbindung herzustellen. Im Allgemeinen ist es nicht zulässig, dass Komponenten startService() aufrufen, um den Dienst zu starten.

Erstellen Sie einen verknüpften Dienst, wenn Sie mit dem Dienst über Aktivitäten und andere Komponenten in Ihrer Anwendung interagieren oder einige Funktionen Ihrer Anwendung über die Inter-Prozess-Kommunikation (IPC) für andere Anwendungen verfügbar machen möchten.

Wenn Sie einen verknüpften Dienst erstellen möchten, implementieren Sie die onBind()-Callback-Methode, um einen IBinder zurückzugeben, der die Schnittstelle für die Kommunikation mit dem Dienst definiert. Andere Anwendungskomponenten können dann bindService() aufrufen, um die Benutzeroberfläche abzurufen und Methoden für den Dienst aufzurufen. Der Dienst dient nur dazu, die an ihn gebundene Anwendungskomponente zu bedienen. Wenn also keine Komponenten an den Dienst gebunden sind, wird er vom System zerstört. Sie müssen einen verknüpften Dienst nicht auf die gleiche Weise beenden wie einen Dienst, der über onStartCommand() gestartet wird.

Wenn Sie einen verknüpften Dienst erstellen möchten, müssen Sie die Schnittstelle definieren, die angibt, wie ein Client mit dem Dienst kommunizieren kann. Diese Schnittstelle zwischen dem Dienst und einem Client muss eine Implementierung von IBinder sein und muss von Ihrem Dienst aus der onBind()-Callback-Methode zurückgegeben werden. Nachdem der Client die IBinder erhalten hat, kann er über diese Schnittstelle mit dem Dienst interagieren.

Mehrere Clients können gleichzeitig an den Dienst gebunden werden. Wenn ein Client mit der Interaktion mit dem Dienst fertig ist, ruft er unbindService() auf, um die Bindung aufzuheben. Wenn dem Dienst keine Clients zugeordnet sind, löscht das System den Dienst.

Es gibt mehrere Möglichkeiten, einen gebundenen Dienst zu implementieren. Die Implementierung ist jedoch komplizierter als bei einem gestarteten Dienst. Aus diesen Gründen wird die Diskussion zu gebundenen Diensten in einem separaten Dokument zu gebundenen Diensten behandelt.

Benachrichtigungen an den Nutzer senden

Wenn ein Dienst ausgeführt wird, kann er den Nutzer über Ereignisse mithilfe von Snackbar-Benachrichtigungen oder Statusleiste-Benachrichtigungen benachrichtigen.

Eine Snackbar-Benachrichtigung ist eine Meldung, die nur für einen kurzen Moment auf der Oberfläche des aktuellen Fensters angezeigt wird, bevor sie verschwindet. Eine Statusleiste enthält ein Symbol mit einer Nachricht, die der Nutzer auswählen kann, um eine Aktion auszuführen (z. B. eine Aktivität zu starten).

Normalerweise ist eine Benachrichtigung in der Statusleiste die beste Methode, wenn Hintergrundarbeiten wie ein Dateidownload abgeschlossen sind und der Nutzer jetzt darauf reagieren kann. Wenn der Nutzer die Benachrichtigung in der maximierten Ansicht auswählt, kann die Benachrichtigung eine Aktivität starten, z. B. die Anzeige der heruntergeladenen Datei.

Lebenszyklus eines Dienstes verwalten

Der Lebenszyklus eines Dienstes ist viel einfacher als der einer Aktivität. Noch wichtiger ist es jedoch, genau darauf zu achten, wie Ihr Dienst erstellt und zerstört wird, da ein Dienst im Hintergrund ausgeführt werden kann, ohne dass der Nutzer es merkt.

Der Dienstlebenszyklus – vom Erstellen bis zum Löschen – kann einen dieser beiden Pfade verfolgen:

  • Einen gestarteten Dienst

    Der Dienst wird erstellt, wenn eine andere Komponente startService() aufruft. Der Dienst wird dann unbegrenzt ausgeführt und muss sich selbst durch Aufrufen von stopSelf() beenden. Eine andere Komponente kann den Dienst auch durch Aufrufen von stopService() beenden. Wenn der Dienst beendet wird, wird er vom System gelöscht.

  • Ein gebundener Dienst

    Der Dienst wird erstellt, wenn eine andere Komponente (ein Client) bindService() aufruft. Der Client kommuniziert dann über eine IBinder-Schnittstelle mit dem Dienst. Der Client kann die Verbindung durch Aufrufen von unbindService() schließen. Mehrere Clients können an denselben Dienst gebunden werden. Wenn alle Clients die Bindung aufheben, löscht das System den Dienst. Der Dienst muss sich nicht selbst beenden.

Diese beiden Pfade sind nicht völlig getrennt. Sie können mit startService() einen bereits gestarteten Dienst binden. Sie können beispielsweise einen Hintergrundmusikdienst starten, indem Sie startService() mit einer Intent aufrufen, die die abzuspielende Musik identifiziert. Später, wenn der Nutzer beispielsweise die Wiedergabe steuern oder Informationen zum aktuellen Titel abrufen möchte, kann eine Aktivität durch Aufrufen von bindService() an den Dienst gebunden werden. In solchen Fällen wird der Dienst durch stopService() oder stopSelf() erst beendet, wenn alle Clients getrennt sind.

Lebenszyklus-Callbacks implementieren

Wie eine Aktivität hat auch ein Dienst Lebenszyklus-Callback-Methoden, die Sie implementieren können, um Änderungen am Status des Dienstes zu überwachen und Aufgaben zur richtigen Zeit auszuführen. Im folgenden Skelettdienst werden die einzelnen Lebenszyklusmethoden veranschaulicht:

Kotlin

class ExampleService : Service() {
    private var startMode: Int = 0             // indicates how to behave if the service is killed
    private var binder: IBinder? = null        // interface for clients that bind
    private var allowRebind: Boolean = false   // indicates whether onRebind should be used

    override fun onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Java

public class ExampleService extends Service {
    int startMode;       // indicates how to behave if the service is killed
    IBinder binder;      // interface for clients that bind
    boolean allowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Hinweis:Im Gegensatz zu den Callback-Methoden für den Aktivitätslebenszyklus müssen Sie nicht die Implementierung der Superklasse dieser Callback-Methoden aufrufen.

Abbildung 2: Der Dienstlebenszyklus. Das Diagramm auf der linken Seite zeigt den Lebenszyklus, wenn der Dienst mit startService() erstellt wird. Das Diagramm auf der rechten Seite zeigt den Lebenszyklus, wenn der Dienst mit bindService() erstellt wird.

Abbildung 2 zeigt die typischen Rückrufmethoden für einen Dienst. In der Abbildung werden zwar Dienste, die von startService() erstellt werden, von denen unterschieden, die von bindService() erstellt werden, aber jeder Dienst, unabhängig davon, wie er gestartet wird, kann Clients die Bindung an ihn ermöglichen. Ein Dienst, der ursprünglich mit onStartCommand() gestartet wurde (durch einen Client, der startService() anruft), kann weiterhin einen Anruf an onBind() erhalten (wenn ein Client bindService() anruft).

Wenn Sie diese Methoden implementieren, können Sie diese beiden verschachtelten Schleifen des Dienstlebenszyklus überwachen:

  • Die gesamte Lebensdauer eines Dienstes liegt zwischen dem Aufruf von onCreate() und der Rückgabe von onDestroy(). Wie bei einer Aktivität erfolgt die Ersteinrichtung eines Dienstes in onCreate() und alle verbleibenden Ressourcen werden in onDestroy() freigegeben. Ein Dienst zur Musikwiedergabe kann beispielsweise den Thread erstellen, in dem die Musik in onCreate() wiedergegeben wird, und ihn dann in onDestroy() beenden.

    Hinweis: Die Methoden onCreate() und onDestroy() werden für alle Dienste aufgerufen, unabhängig davon, ob sie mit startService() oder bindService() erstellt wurden.

  • Die aktive Lebensdauer eines Dienstes beginnt mit einem Aufruf von onStartCommand() oder onBind(). Jede Methode erhält die Intent, die entweder an startService() oder bindService() übergeben wurde.

    Wenn der Dienst gestartet wird, endet die aktive Lebensdauer gleichzeitig mit der gesamten Lebensdauer. Der Dienst ist also auch nach der Rückgabe von onStartCommand() noch aktiv. Wenn der Dienst gebunden ist, endet die aktive Lebensdauer, wenn onUnbind() zurückgegeben wird.

Hinweis:Ein gestarteter Dienst wird zwar durch einen Aufruf von stopSelf() oder stopService() beendet, es gibt aber keinen entsprechenden Rückruf für den Dienst (kein onStop()-Rückruf). Sofern der Dienst nicht an einen Client gebunden ist, wird er vom System zerstört, wenn der Dienst beendet wird. onDestroy() ist der einzige empfangene Rückruf.

Weitere Informationen zum Erstellen eines Dienstes, der eine Bindung bereitstellt, finden Sie im Dokument Bound Services (gebundene Dienste). Im Abschnitt Lifecycle of a Bound Service (Lebenszyklus eines gebundenen Dienstes) finden Sie weitere Informationen zur onRebind()-Callback-Methode.