Présentation des services

Un Service est un composant d'application qui peut effectuer des opérations de longue durée en arrière-plan. Elle ne fournit pas d'interface utilisateur. Une fois démarré, un service peut continuer à s'exécuter pendant un certain temps, même lorsque l'utilisateur passe à une autre application. En outre, un composant peut être lié à un service pour interagir avec lui et même effectuer une communication inter-processus (IPC). Par exemple, un service peut gérer des transactions réseau, lire de la musique, effectuer des E/S de fichiers ou interagir avec un fournisseur de contenu, le tout en arrière-plan.

Attention:Un service s'exécute dans le thread principal de son processus d'hébergement. Le service ne crée pas son propre thread et ne s'exécute pas dans un processus distinct, sauf indication contraire de votre part. Vous devez exécuter toutes les opérations de blocage sur un thread distinct du service pour éviter les erreurs ANR (L'application ne répond pas).

Types de services

Voici les trois différents types de services:

Premier plan

Un service de premier plan effectue une opération que l'utilisateur peut remarquer. Par exemple, une application audio peut utiliser un service de premier plan pour lire une piste audio. Les services de premier plan doivent afficher une notification. Les services de premier plan continuent de s'exécuter même lorsque l'utilisateur n'interagit pas avec l'application.

Lorsque vous utilisez un service de premier plan, vous devez afficher une notification afin d'informer les utilisateurs que le service est en cours d'exécution. Cette notification ne peut être ignorée que si le service est arrêté ou supprimé au premier plan.

Découvrez comment configurer des services de premier plan dans votre application.

Remarque:L'API WorkManager offre un moyen flexible de planifier des tâches. Elle peut exécuter ces tâches en tant que services de premier plan si nécessaire. Dans de nombreux cas, il est préférable d'utiliser WorkManager plutôt que d'utiliser directement les services de premier plan.

Arrière-plan
Un service d'arrière-plan effectue une opération que l'utilisateur ne remarque pas directement. Par exemple, si une application utilise un service pour compacter son stockage, il s'agit généralement d'un service d'arrière-plan.

Remarque:Si votre application cible le niveau d'API 26 ou supérieur, le système impose des restrictions concernant l'exécution des services d'arrière-plan lorsque l'application elle-même n'est pas au premier plan. Dans la plupart des cas, par exemple, vous ne devez pas accéder aux informations de localisation en arrière-plan. À la place, planifiez des tâches à l'aide de WorkManager.

Associé
Un service est lié lorsqu'un composant d'application s'y lie en appelant bindService(). Un service lié offre une interface client-serveur qui permet aux composants d'interagir avec le service, d'envoyer des requêtes, de recevoir des résultats et même de le faire entre les processus à l'aide de la communication inter-processus (IPC). Un service lié ne s'exécute que tant qu'un autre composant d'application y est lié. Plusieurs composants peuvent être liés au service à la fois, mais lorsqu'ils sont tous dissociés, le service est détruit.

Bien que cette documentation traite généralement des services démarrés et liés séparément, votre service peut fonctionner dans les deux manières. Il peut être démarré (pour s'exécuter indéfiniment) et autoriser la liaison. Il s'agit simplement d'implémenter ou non deux méthodes de rappel: onStartCommand() pour permettre aux composants de la démarrer et onBind() pour autoriser la liaison.

Que votre service soit démarré, lié ou les deux, tout composant d'application peut utiliser le service (même à partir d'une application distincte) de la même manière qu'un composant peut utiliser une activité, en le démarrant avec un élément Intent. Toutefois, vous pouvez déclarer le service comme privé dans le fichier manifeste et bloquer l'accès des autres applications. Ce point est abordé plus en détail dans la section Déclarer le service dans le fichier manifeste.

Choisir entre un service et un thread

Un service est simplement un composant qui peut s'exécuter en arrière-plan, même lorsque l'utilisateur n'interagit pas avec votre application. Vous ne devez donc créer un service que si c'est ce dont vous avez besoin.

Si vous devez effectuer des tâches en dehors de votre thread principal, mais uniquement lorsque l'utilisateur interagit avec votre application, vous devez créer un thread dans le contexte d'un autre composant d'application. Par exemple, si vous souhaitez écouter de la musique, mais uniquement lorsque votre activité est en cours d'exécution, vous pouvez créer un thread dans onCreate(), commencer à l'exécuter dans onStart(), puis l'arrêter dans onStop(). Envisagez également d'utiliser des pools de threads et des exécuteurs du package java.util.concurrent ou des coroutines Kotlin au lieu de la classe Thread traditionnelle. Pour en savoir plus sur le déplacement de l'exécution vers des threads en arrière-plan, consultez le document Exécution de threads sur Android.

N'oubliez pas que si vous utilisez un service, il s'exécute toujours par défaut dans le thread principal de votre application. Vous devez donc toujours créer un thread dans le service s'il effectue des opérations intensives ou bloquantes.

Principes de base

Pour créer un service, vous devez créer une sous-classe de Service ou utiliser l'une de ses sous-classes existantes. Dans votre implémentation, vous devez remplacer certaines méthodes de rappel qui gèrent des aspects clés du cycle de vie du service et fournir un mécanisme permettant aux composants de se connecter au service, le cas échéant. Voici les méthodes de rappel les plus importantes que vous devez remplacer:

onStartCommand()
Le système appelle cette méthode en appelant startService() lorsqu'un autre composant (comme une activité) demande le démarrage du service. Lorsque cette méthode s'exécute, le service est démarré et peut s'exécuter en arrière-plan indéfiniment. Si vous implémentez cela, il est de votre responsabilité d'arrêter le service une fois son travail terminé en appelant stopSelf() ou stopService(). Si vous souhaitez uniquement fournir une liaison, vous n'avez pas besoin d'implémenter cette méthode.
onBind()
Le système appelle cette méthode en appelant bindService() lorsqu'un autre composant souhaite se lier au service (par exemple, pour effectuer un RPC). Dans la mise en œuvre de cette méthode, vous devez fournir une interface permettant aux clients de communiquer avec le service en renvoyant un IBinder. Vous devez toujours implémenter cette méthode. Toutefois, si vous ne souhaitez pas autoriser la liaison, vous devez renvoyer la valeur "null".
onCreate()
Le système appelle cette méthode pour effectuer des procédures de configuration ponctuelles lors de la création initiale du service (avant d'appeler onStartCommand() ou onBind()). Si le service est déjà en cours d'exécution, cette méthode n'est pas appelée.
onDestroy()
Le système appelle cette méthode lorsque le service n'est plus utilisé et qu'il est détruit. Votre service doit le mettre en œuvre pour nettoyer toutes les ressources telles que les threads, les écouteurs enregistrés ou les récepteurs. Il s'agit du dernier appel reçu par le service.

Si un composant démarre le service en appelant startService() (ce qui entraîne un appel à onStartCommand()), le service continue de s'exécuter jusqu'à ce qu'il s'arrête avec stopSelf() ou qu'un autre composant l'arrête en appelant stopService().

Si un composant appelle bindService() pour créer le service et que onStartCommand() n'est pas appelé, le service s'exécute uniquement tant que le composant y est lié. Une fois le service dissocié de tous ses clients, le système le détruit.

Le système Android n'arrête un service que lorsque la mémoire est faible et doit récupérer les ressources système pour l'activité ciblée par l'utilisateur. Si le service est lié à une activité axée sur l'utilisateur, il est moins susceptible d'être arrêté. S'il est déclaré pour s'exécuter au premier plan, il est rarement arrêté. Si le service est démarré et qu'il s'exécute depuis longtemps, le système diminue sa position dans la liste des tâches en arrière-plan au fil du temps, et le service devient très sujet à la fermeture. S'il est démarré, vous devez le concevoir pour qu'il puisse gérer correctement les redémarrages du système. Si le système arrête votre service, il le redémarre dès que des ressources sont disponibles, mais cela dépend également de la valeur renvoyée par onStartCommand(). Pour en savoir plus sur les cas où le système peut détruire un service, consultez le document Processus et threads.

Dans les sections suivantes, vous verrez comment créer les méthodes de service startService() et bindService(), et comment les utiliser à partir d'autres composants d'application.

Déclarer un service dans le fichier manifeste

Vous devez déclarer tous les services dans le fichier manifeste de votre application, comme vous le faites pour les activités et les autres composants.

Pour déclarer votre service, ajoutez un élément <service> en tant qu'enfant de l'élément <application>. Voici un exemple :

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

Pour en savoir plus sur la déclaration de votre service dans le fichier manifeste, consultez la documentation de référence sur l'élément <service>.

Vous pouvez inclure d'autres attributs dans l'élément <service> pour définir des propriétés telles que les autorisations requises pour démarrer le service et le processus dans lequel il doit s'exécuter. L'attribut android:name est le seul attribut obligatoire. Il spécifie le nom de classe du service. Après avoir publié votre application, ne modifiez pas ce nom pour éviter de briser le code en raison de la dépendance à des intents explicites pour démarrer ou lier le service (consultez l'article de blog Things That Does That Invalid Change).

Attention: Pour vous assurer que votre application est sécurisée, utilisez toujours un intent explicite lorsque vous démarrez Service et ne déclarez pas de filtres d'intent pour vos services. L'utilisation d'un intent implicite pour démarrer un service représente un risque de sécurité, car vous ne pouvez pas être certain du service qui répond à l'intent, et l'utilisateur ne peut pas voir quel service démarre. À partir d'Android 5.0 (niveau d'API 21), le système génère une exception si vous appelez bindService() avec un intent implicite.

Pour vous assurer que votre service n'est disponible que pour votre application, incluez l'attribut android:exported et définissez-le sur false. Cela empêche les autres applications de démarrer votre service, même lorsque vous utilisez un intent explicite.

Remarque : Les utilisateurs peuvent voir les services en cours d'exécution sur leur appareil. S'il voit un service qu'il ne reconnaît pas ou ne fait pas confiance, il peut l'arrêter. Pour éviter que votre service ne soit arrêté accidentellement par les utilisateurs, vous devez ajouter l'attribut android:description à l'élément <service> dans le fichier manifeste de votre application. Dans la description, fournissez une courte phrase expliquant ce que fait le service et les avantages qu'il offre.

Créer un service démarré

Un service démarré est un service qu'un autre composant commence par appeler startService(), ce qui entraîne un appel de la méthode onStartCommand() du service.

Lorsqu'un service est démarré, son cycle de vie est indépendant du composant qui l'a démarré. Le service peut s'exécuter en arrière-plan indéfiniment, même si le composant qui l'a démarré est détruit. Par conséquent, le service doit s'arrêter une fois sa tâche terminée en appelant stopSelf() ou un autre composant peut l'arrêter en appelant stopService().

Un composant d'application tel qu'une activité peut démarrer le service en appelant startService() et en transmettant un Intent qui spécifie le service et inclut toutes les données qu'il doit utiliser. Le service reçoit ce Intent dans la méthode onStartCommand().

Par exemple, supposons qu'une activité doive enregistrer certaines données dans une base de données en ligne. L'activité peut démarrer un service associé et lui fournir les données à enregistrer en transmettant un intent à startService(). Le service reçoit l'intent dans onStartCommand(), se connecte à Internet et effectue la transaction de base de données. Une fois la transaction terminée, le service s'arrête et est détruit.

Attention:Un service s'exécute dans le même processus que l'application dans laquelle il est déclaré et dans le thread principal de cette application par défaut. Si votre service effectue des opérations intensives ou bloquantes pendant que l'utilisateur interagit avec une activité de la même application, il ralentit les performances de l'activité. Pour éviter tout impact sur les performances de l'application, démarrez un nouveau thread dans le service.

La classe Service est la classe de base de tous les services. Lorsque vous étendez cette classe, il est important de créer un thread dans lequel le service peut effectuer l'ensemble de son travail. Le service utilise par défaut le thread principal de votre application, ce qui peut ralentir les performances de toute activité en cours d'exécution de votre application.

Le framework Android fournit également la sous-classe IntentService de Service qui utilise un thread de nœud de calcul pour gérer toutes les requêtes de démarrage, une par une. L'utilisation de cette classe n'est pas recommandée pour les nouvelles applications, car elle ne fonctionnera pas correctement à partir d'Android 8 Oreo, en raison de l'introduction de limites d'exécution en arrière-plan. De plus, elle est obsolète à partir d'Android 11. Vous pouvez utiliser JobIntentService en remplacement de IntentService, qui est compatible avec les versions plus récentes d'Android.

Les sections suivantes décrivent comment implémenter votre propre service personnalisé, mais nous vous recommandons vivement d'utiliser WorkManager dans la plupart des cas d'utilisation. Consultez le guide sur le traitement en arrière-plan sur Android pour voir s'il existe une solution adaptée à vos besoins.

Étendre la classe Service

Vous pouvez étendre la classe Service pour gérer chaque intent entrant. Voici à quoi pourrait ressembler une implémentation de base:

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

L'exemple de code gère tous les appels entrants dans onStartCommand() et publie le travail dans un Handler exécuté sur un thread d'arrière-plan. Il fonctionne comme un IntentService et traite toutes les requêtes en série, les unes après les autres. Vous pouvez modifier le code pour exécuter la tâche sur un pool de threads, par exemple si vous souhaitez exécuter plusieurs requêtes simultanément.

Notez que la méthode onStartCommand() doit renvoyer un nombre entier. L'entier est une valeur qui décrit comment le système doit poursuivre le service s'il est arrêté par le système. La valeur renvoyée par onStartCommand() doit être l'une des constantes suivantes:

START_NOT_STICKY
Si le système arrête le service après le retour de onStartCommand(), ne recréez pas le service, sauf s'il existe des intents en attente de diffusion. Il s'agit de l'option la plus sûre pour éviter d'exécuter votre service lorsque cela n'est pas nécessaire et lorsque votre application peut simplement redémarrer les tâches inachevées.
START_STICKY
Si le système arrête le service après le retour de onStartCommand(), recréez le service et appelez onStartCommand(), mais ne remettez pas le dernier intent. À la place, le système appelle onStartCommand() avec un intent nul, sauf si des intents sont en attente pour démarrer le service. Dans ce cas, ces intents sont transmis. Cela convient aux lecteurs multimédias (ou à des services similaires) qui n'exécutent pas de commandes, mais s'exécutent indéfiniment et attendent une tâche.
START_REDELIVER_INTENT
Si le système arrête le service après le retour de onStartCommand(), recréez le service et appelez onStartCommand() avec le dernier intent envoyé au service. Tous les intents en attente sont transmis l'un après l'autre. Cela convient aux services qui effectuent activement une tâche qui doit être réactivée immédiatement, telle que le téléchargement d'un fichier.

Pour en savoir plus sur ces valeurs renvoyées, consultez la documentation de référence associée à chaque constante.

Démarrer un service

Vous pouvez démarrer un service à partir d'une activité ou d'un autre composant d'application en transmettant un Intent à startService() ou startForegroundService(). Le système Android appelle la méthode onStartCommand() du service et lui transmet le Intent, qui spécifie le service à démarrer.

Remarque: Si votre application cible le niveau d'API 26 ou supérieur, le système impose des restrictions sur l'utilisation ou la création de services d'arrière-plan, sauf si l'application elle-même se trouve au premier plan. Si une application doit créer un service de premier plan, elle doit appeler startForegroundService(). Cette méthode crée un service d'arrière-plan, mais elle indique au système que le service va se promouvoir au premier plan. Une fois le service créé, il doit appeler sa méthode startForeground() dans les cinq secondes.

Par exemple, une activité peut démarrer l'exemple de service de la section précédente (HelloService) à l'aide d'un intent explicite avec startService(), comme indiqué ci-dessous:

Kotlin

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

Java

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

La méthode startService() est renvoyée immédiatement, et le système Android appelle la méthode onStartCommand() du service. Si le service n'est pas déjà en cours d'exécution, le système appelle d'abord onCreate(), puis onStartCommand().

Si le service ne fournit pas également de liaison, l'intent transmis avec startService() est le seul mode de communication entre le composant d'application et le service. Toutefois, si vous souhaitez que le service renvoie un résultat, le client qui démarre le service peut créer un PendingIntent pour une diffusion (avec getBroadcast()) et le transmettre au service dans l'Intent qui lance le service. Le service peut ensuite utiliser la diffusion pour fournir un résultat.

Les requêtes multiples pour démarrer le service entraînent plusieurs appels correspondants au onStartCommand() du service. Cependant, une seule requête d'arrêt du service (avec stopSelf() ou stopService()) est nécessaire.

Arrêter un service

Un service démarré doit gérer son propre cycle de vie. Autrement dit, le système n'arrête pas et ne détruise pas le service, sauf s'il doit récupérer de la mémoire système et que le service continue de s'exécuter après le retour de onStartCommand(). Le service doit s'arrêter en appelant stopSelf() ou un autre composant peut l'arrêter en appelant stopService().

Lorsque l'arrêt avec stopSelf() ou stopService() est demandé, le système détruit le service dès que possible.

Si votre service traite plusieurs requêtes vers onStartCommand() simultanément, vous ne devez pas l'arrêter une fois le traitement d'une requête de démarrage terminé, car vous pourriez avoir reçu une nouvelle requête de démarrage (l'arrêt à la fin de la première requête mettrait fin à la seconde). Pour éviter ce problème, vous pouvez utiliser stopSelf(int) afin que votre requête d'arrêt du service soit toujours basée sur la requête de démarrage la plus récente. Autrement dit, lorsque vous appelez stopSelf(int), vous transmettez l'ID de la requête de démarrage (startId envoyé à onStartCommand()) à laquelle votre requête d'arrêt correspond. Ensuite, si le service reçoit une nouvelle requête de démarrage avant que vous ne puissiez appeler stopSelf(int), l'ID ne correspond pas et le service ne s'arrête pas.

Attention:Pour éviter de gaspiller des ressources système et de consommer de la batterie, assurez-vous que votre application arrête ses services une fois qu'elle a terminé de fonctionner. Si nécessaire, d'autres composants peuvent arrêter le service en appelant stopService(). Même si vous activez la liaison pour le service, vous devez toujours arrêter le service vous-même s'il reçoit un appel à onStartCommand().

Pour en savoir plus sur le cycle de vie d'un service, consultez la section ci-dessous sur la gestion du cycle de vie d'un service.

Créer un service lié

Un service lié permet aux composants d'application de s'y associer en appelant bindService() pour créer une connexion de longue durée. Il n'autorise généralement pas les composants à le démarrer en appelant startService().

Créez un service lié lorsque vous souhaitez interagir avec le service à partir d'activités et d'autres composants de votre application, ou pour exposer certaines fonctionnalités de votre application à d'autres applications via la communication inter-processus (IPC).

Pour créer un service lié, implémentez la méthode de rappel onBind() afin de renvoyer un IBinder qui définit l'interface de communication avec le service. D'autres composants d'application peuvent ensuite appeler bindService() pour récupérer l'interface et commencer à appeler des méthodes sur le service. Le service ne vit que pour diffuser le composant d'application qui lui est lié. Par conséquent, lorsqu'aucun composant n'est lié au service, le système le détruit. Vous n'avez pas besoin d'arrêter un service lié de la même manière que lorsque le service est démarré via onStartCommand().

Pour créer un service lié, vous devez définir l'interface spécifiant la manière dont un client peut communiquer avec le service. Cette interface entre le service et un client doit être une implémentation de IBinder et c'est ce que votre service doit renvoyer à partir de la méthode de rappel onBind(). Une fois que le client a reçu le IBinder, il peut commencer à interagir avec le service via cette interface.

Plusieurs clients peuvent s'associer au service simultanément. Lorsqu'un client a fini d'interagir avec le service, il appelle unbindService() pour annuler la liaison. Si aucun client n'est lié au service, le système le détruit.

Il existe plusieurs façons de mettre en œuvre un service lié, et l'implémentation est plus complexe qu'avec un service démarré. C'est pourquoi la discussion sur les services liés figure dans un document distinct sur les services liés.

Envoyer des notifications à l'utilisateur

Lorsqu'un service est en cours d'exécution, il peut informer l'utilisateur d'événements à l'aide de notifications de snackbar ou de notifications de barre d'état.

Une notification de snackbar est un message qui s'affiche à la surface de la fenêtre actuelle pendant un moment avant de disparaître. Une notification de barre d'état fournit une icône dans la barre d'état avec un message que l'utilisateur peut sélectionner pour effectuer une action (comme démarrer une activité).

En règle générale, une notification dans la barre d'état est la meilleure technique à utiliser lorsque le travail en arrière-plan, comme le téléchargement d'un fichier, est terminé, et que l'utilisateur peut désormais agir en conséquence. Lorsque l'utilisateur sélectionne la notification dans la vue développée, elle peut lancer une activité (par exemple, afficher le fichier téléchargé).

Gérer le cycle de vie d'un service

Le cycle de vie d'un service est beaucoup plus simple que celui d'une activité. Cependant, il est encore plus important de prêter une attention particulière à la manière dont votre service est créé et détruit, car un service peut s'exécuter en arrière-plan à l'insu de l'utilisateur.

Le cycle de vie du service, de sa création à sa destruction, peut suivre l'un des deux chemins suivants:

  • Un service démarré

    Le service est créé lorsqu'un autre composant appelle startService(). Le service s'exécute ensuite indéfiniment et doit s'arrêter en appelant stopSelf(). Un autre composant peut également arrêter le service en appelant stopService(). Lorsque le service est arrêté, le système le détruit.

  • Un service lié

    Le service est créé lorsqu'un autre composant (un client) appelle bindService(). Le client communique ensuite avec le service via une interface IBinder. Le client peut fermer la connexion en appelant unbindService(). Plusieurs clients peuvent être liés au même service et, lorsque tous les deux sont dissociés, le système détruit le service. Le service n'a pas besoin de s'arrêter.

Ces deux chemins ne sont pas complètement séparés. Vous pouvez créer une liaison avec un service déjà démarré avec startService(). Par exemple, vous pouvez démarrer un service de musique en arrière-plan en appelant startService() avec un Intent qui identifie la musique à lire. Par la suite, si l'utilisateur souhaite exercer un certain contrôle sur le lecteur ou obtenir des informations sur le titre en cours, une activité peut être liée au service en appelant bindService(). Dans ce cas, stopService() ou stopSelf() n'arrête pas le service tant que tous les clients ne sont pas dissociés.

Implémenter les rappels de cycle de vie

Comme pour une activité, un service dispose de méthodes de rappel de cycle de vie que vous pouvez implémenter pour surveiller les changements d'état du service et effectuer des tâches aux moments appropriés. Le service de squelette suivant présente chacune des méthodes du cycle de vie:

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

Remarque:Contrairement aux méthodes de rappel du cycle de vie d'une activité, vous n'avez pas besoin d'appeler l'implémentation de super-classe de ces méthodes de rappel.

Figure 2. Cycle de vie du service. Le schéma de gauche illustre le cycle de vie lorsque le service est créé avec startService(), tandis que celui de droite illustre le cycle de vie lorsque le service est créé avec bindService().

La figure 2 illustre les méthodes de rappel classiques pour un service. Bien que la figure sépare les services créés par startService() de ceux créés par bindService(), n'oubliez pas que n'importe quel service, quelle que soit la façon dont il a été démarré, peut potentiellement permettre aux clients de s'y associer. Un service démarré par onStartCommand() (par un client appelant startService()) peut toujours recevoir un appel à onBind() (lorsqu'un client appelle bindService()).

En implémentant ces méthodes, vous pouvez surveiller ces deux boucles imbriquées du cycle de vie du service:

  • La durée de vie complète d'un service se produit entre le moment où onCreate() est appelé et le moment où onDestroy() est renvoyé. Comme une activité, un service effectue sa configuration initiale dans onCreate() et libère toutes les ressources restantes dans onDestroy(). Par exemple, un service de lecture musicale peut créer le thread sur lequel la musique est diffusée dans onCreate(), puis l'arrêter dans onDestroy().

    Remarque: Les méthodes onCreate() et onDestroy() sont appelées pour tous les services, qu'ils soient créés par startService() ou bindService().

  • La durée de vie active d'un service commence par un appel à onStartCommand() ou onBind(). Chaque méthode reçoit le Intent qui a été transmis à startService() ou à bindService().

    Si le service est démarré, la durée de vie active se termine en même temps que la durée de vie complète (le service reste actif même après le retour de onStartCommand()). Si le service est lié, la durée de vie active se termine lorsque onUnbind() est renvoyé.

Remarque:Bien qu'un service démarré soit arrêté par un appel à stopSelf() ou stopService(), il n'y a pas de rappel correspondant pour le service (il n'y a pas de rappel onStop()). À moins que le service ne soit lié à un client, le système le détruit lorsque le service est arrêté. onDestroy() est le seul rappel reçu.

Pour en savoir plus sur la création d'un service qui fournit des liaisons, consultez le document Services liés. Vous y trouverez davantage d'informations sur la méthode de rappel onRebind() dans la section Gérer le cycle de vie d'un service lié.