Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

Visão geral dos serviços

Um Service é um componente do aplicativo que pode realizar operações longas e não fornece uma interface do usuário. Outro componente do aplicativo pode iniciar um serviço e ele continuará em execução em segundo plano mesmo que o usuário alterne para outro aplicativo. Além disso, um componente poderá se vincular a um serviço para interagir com ele e até estabelecer comunicação entre processos (IPC). Por exemplo, um serviço pode lidar com transações de rede, reproduzir música, executar E/S de arquivos ou interagir com um provedor de conteúdo, tudo a partir do segundo plano.

Veja os três tipos diferentes de serviços:

Primeiro plano
O serviço em primeiro plano realiza uma operação que é perceptível ao usuário. Por exemplo, um aplicativo de áudio usaria um serviço em primeiro plano para reproduzir uma faixa de áudio. Serviços em primeiro plano precisam exibir uma Notificação. Serviços em primeiro plano continuam a executar mesmo se o usuário não estiver interagindo com o aplicativo.
Segundo plano
O serviço em segundo plano realiza uma operação que não é perceptível ao usuário. Por exemplo, o serviço usado por um aplicativo para compactar armazenamento normalmente é de segundo plano.

Observação: se seu aplicativo é direcionado à API de nível 26 ou posterior, o sistema impõe restrições à execução de serviços em segundo plano quando o próprio aplicativo não estiver em primeiro plano. Na maioria desses casos, o aplicativo usará um job programado.

Vinculado
Um serviço é vinculado quando um componente do aplicativo chama bindService() para vinculá-lo. Um serviço vinculado oferece uma interface servidor-cliente que permite que os componentes interajam com a interface, enviem solicitações, recebam resultados, mesmo em processos com comunicação interprocessual (IPC). Um serviço vinculado permanece em execução somente enquanto outro componente do aplicativo estiver vinculado a ele. Vários componentes podem ser vinculados ao serviço de uma só vez, mas quando todos desfizerem o vínculo, o serviço será eliminado.

Embora essa documentação geralmente discuta os serviços iniciados e vinculados separadamente, seu serviço pode funcionar das duas formas — pode ser iniciado (para executar indefinidamente) e também permitir vinculação. Basta você implementar alguns métodos de callback: onStartCommand() para permitir que os componentes iniciem o serviço e onBind() para permitir a vinculação.

Se o serviço for iniciado, vinculado ou ambos, qualquer componente do aplicativo poderá usar o serviço (mesmo a partir de um aplicativo separado), da mesma forma que qualquer componente poderá usar uma atividade — iniciando com um Intent. No entanto, é possível declarar o serviço como privado no arquivo do manifesto, e bloquear o acesso de outros aplicativos. Isso é discutido com mais detalhes na seção sobre Declaração do serviço no manifesto.

Atenção: um serviço é executado no thread principal em seu processo de hospedagem. O serviço não cria seu próprio thread e não é executado em um processo separado (salvo se especificado). Isso significa que, se o serviço for realizar qualquer trabalho intensivo de CPU ou operações de bloqueio (como reprodução de MP3 ou rede), você precisará criar um novo thread dentro do serviço para concluir a operação. Ao usar um thread separado, você reduzirá o risco da ocorrência de erros de Application Not Responding (ANR) e o thread principal do aplicativo poderá permanecer dedicado à interação do usuário com as atividades.

Como escolher entre um serviço e um thread

Um serviço é simplesmente um componente que pode ser executado em segundo plano mesmo quando o usuário não está interagindo com o aplicativo. Sendo assim, crie um serviço somente se é isso que você quer fazer.

Caso precise realizar trabalhos fora do thread principal, mas somente enquanto o usuário estiver interagindo com o aplicativo, crie um novo thread, e não um serviço. Por exemplo: se quiser reproduzir músicas, mas somente enquanto a atividade estiver em execução, você pode criar um thread em onCreate(), iniciando a execução em onStart() e interrompendo em onStop(). Considere também usar AsyncTask ou HandlerThread, em vez da classe tradicional Thread. Consulte o documento Processos e threads para mais informações sobre threads.

Lembre-se de que, se usar um serviço, ele ainda será executado no thread principal do aplicativo por padrão. Portanto, crie um novo thread dentro do serviço se ele realizar operações intensivas ou de bloqueio.

Conceitos básicos

Para criar um serviço, você precisa criar uma subclasse de Service (ou uma das subclasses existentes). Na implementação, será necessário modificar alguns métodos de callback que lidem com aspectos essenciais do ciclo de vida do serviço e forneçam um mecanismo para vincular componentes ao serviço, se apropriado. Veja os métodos de callback mais importantes a modificar:

onStartCommand()
O sistema invoca esse método quando outro componente, como uma atividade, solicita que o serviço seja iniciado, chamando startService(). Quando esse método é executado, o serviço é iniciado e pode ser executado em segundo plano indefinidamente. Se implementar isso, é de sua responsabilidade interromper o serviço quando o trabalho for concluído, chamando stopSelf() ou stopService(). Caso queira somente fornecer a vinculação, não é necessário implementar esse método.
onBind()
O sistema invoca esse método quando outro componente quer se vincular ao serviço (como para realizações de RPC) chamando bindService(). Na implementação desse método, você precisa fornecer uma interface que os clientes usem para se comunicar com o serviço, retornando um IBinder. Você precisa sempre implementar esse método. No entanto, se não quiser permitir a vinculação, retorne o valor nulo.
onCreate()
O sistema invoca esse método para realizar procedimentos únicos de configuração (antes de chamar onStartCommand() ou onBind()). Se o serviço já estiver em execução, esse método não é chamado.
onDestroy()
O sistema invoca esse método quando o serviço não é mais usado e está sendo eliminado. O serviço precisa implementar isso para limpar quaisquer recursos, como threads, listeners registrados ou receptores. Essa é a última chamada que o serviço recebe.

Se um componente iniciar o serviço chamando startService() (o que resulta em uma chamada para onStartCommand()), o serviço permanecerá em execução até ser interrompido por conta própria com stopSelf() ou por outro componente chamando stopService().

Se um componente chamar bindService() para criar o serviço (e onStartCommand() não for chamado), o serviço será executado somente enquanto o componente estiver vinculado a ele. Após o serviço ser desvinculado de todos os clientes, o sistema o eliminará.

O sistema Android forçará a interrupção de um serviço somente quando a memória estiver baixa e precisar recuperar os recursos do sistema para a atividade que tiver o foco do usuário. Se o serviço estiver vinculado a uma atividade a que o usuário tenha atribuído foco, é menos provável que ele seja eliminado. E, se o serviço for declarado para ser executado em primeiro plano, ele quase nunca será eliminado. Se o serviço for iniciado e estiver em uma longa execução, o sistema reduzirá sua posição na lista de tarefas em segundo plano no decorrer do tempo e o serviço se tornará altamente suscetível à eliminação — se o serviço for iniciado, você precisará programá-lo para lidar com reinicializações pelo sistema. Se o sistema eliminar seu serviço, ele reiniciará assim que os recursos estiverem disponíveis novamente, mas isso também depende do valor que você retornar de onStartCommand(). Para mais informações sobre quando o sistema pode eliminar um serviço, consulte o documento Processos e threads.

Nas seções a seguir, você verá como criar os métodos de serviço startService() e bindService(). Além disso, descobrirá como usá-los a partir dos componentes de outro aplicativo.

Declaração de serviço no manifesto

Você precisa declarar todos os serviços no arquivo de manifesto do aplicativo, assim como fez com as atividades e com outros componentes.

Para declarar o serviço, adicione um elemento <service> como filho do elemento <application>. Vejamos um exemplo:

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

Consulte a referência de elemento <service> para mais informações sobre como declarar o serviço no manifesto.

É possível incluir outros atributos no elemento <service> para definir propriedades como permissões necessárias para iniciar o serviço e o processo em que o serviço será executado. O único atributo necessário é android:name — ele especifica o nome da classe do serviço. Após a publicação do aplicativo, não altere o nome. Isso evita o risco de separação do código devido à dependência em intents explícitos para iniciar ou vincular o serviço. Leia a postagem do blog Coisas que não podem ser alteradas.

Atenção: para garantir a segurança do seu aplicativo, sempre use um intent explícito ao iniciar um Service e não declare filtros de intents para os serviços. O uso de um intent implícito para iniciar um serviço representa um risco de segurança, porque não é possível determinar qual serviço responderá ao intent, e o usuário não poderá ver qual serviço é iniciado. A partir do Android 5.0 (API de nível 21), o sistema lança uma exceção ao chamar bindService() com um intent implícito.

É possível garantir que o serviço esteja disponível somente para o aplicativo incluindo o atributo android:exported e definindo-o como false. Isso impede efetivamente que outros aplicativos iniciem o serviço, mesmo usando um intent explícito.

Observação: os usuários podem ver quais serviços estão em execução no dispositivo. Ao ver um serviço que não reconhecem ou em que não confiam, eles podem interrompê-lo. Para evitar que seu serviço seja interrompido acidentalmente pelos usuários, você precisa adicionar o atributo android:description ao elemento <service> no manifesto do aplicativo. Na descrição, explique em poucas palavras os benefícios do serviço e o que ele faz.

Criação de um serviço iniciado

Um serviço iniciado é o que outro componente inicia chamando startService(), o que resulta em uma chamada para o método onStartCommand() do serviço.

Quando um serviço é iniciado, ele tem um ciclo de vida que não depende do componente que o iniciou. O serviço pode ficar em execução em segundo plano indefinidamente, mesmo que o componente que o iniciou seja eliminado. Além disso, o serviço será interrompido quando o job for concluído, chamando stopSelf(). Outros componentes podem interrompê-lo chamando stopService().

Um componente do aplicativo, como uma atividade, pode iniciar o serviço chamando startService() e passando Intent, que especifica o serviço e inclui os dados para o serviço usar. O serviço recebe esse Intent no método onStartCommand().

Por exemplo, imagine que uma atividade precise salvar alguns dados em um banco de dados on-line. A atividade pode iniciar um serviço de acompanhamento e entregar a ele os dados a salvar passando um intent para startService(). O serviço recebe o intent em onStartCommand(), conecta-se à internet e realiza a transação no banco de dados. Quando a transação é concluída, o serviço é interrompido e eliminado.

Atenção: por padrão, um serviço é executado no mesmo processo que o aplicativo em que ele é declarado e no thread principal do aplicativo. Se o serviço realizar operações intensivas ou de bloqueio quando o usuário interagir com uma atividade do mesmo aplicativo, o serviço diminuirá o desempenho da atividade. Para evitar impacto no desempenho do aplicativo, inicie um novo thread dentro do serviço.

Geralmente, há duas classes que podem ser estendidas para criar um serviço iniciado:

Service
Essa é a classe de base para todos os serviços. Ao estender essa classe, é importante criar um novo thread onde o serviço possa completar o trabalho. Por padrão, o serviço usa o thread principal do aplicativo, o que pode reduzir a velocidade do desempenho de qualquer atividade executada.
IntentService
Essa é uma subclasse de Service que usa um thread de trabalho para lidar com todas as solicitações de inicialização, uma por vez. Essa é a melhor opção se não quiser que o serviço lide com várias solicitações simultaneamente. Implemente o onHandleIntent(), que recebe o intent para cada solicitação de inicialização. Assim, você pode concluir o trabalho em segundo plano.

As seguintes seções descrevem como é possível implementar o serviço usando uma dessas classes.

Extensão da classe IntentService

Como a maioria dos serviços não precisa lidar com várias solicitações simultaneamente (o que pode ser perigoso para situações de vários threads), é melhor se o serviço for implementado usando a classe IntentService.

A classe IntentService executa as seguintes ações:

  • Cria um thread de trabalho padrão que executa todos os intents entregues a onStartCommand() separado do thread principal do aplicativo.
  • Cria uma fila de trabalho que passa um intent por vez à implementação onHandleIntent(), para que nunca seja necessário preocupar-se com vários threads.
  • Interrompe o serviço depois que todas as solicitações forem resolvidas para que não seja necessário chamar stopSelf().
  • Fornece uma implementação padrão de onBind() que retorna como nulo.
  • Fornece uma implementação padrão de onStartCommand() que envia o intent para a fila de trabalho e, em seguida, para a implementação de onHandleIntent().

Para concluir o trabalho fornecido pelo cliente, implemente onHandleIntent(). No entanto, também é necessário fornecer um pequeno construtor para o serviço.

Veja um exemplo de implementação de IntentService:

Kotlin

/**
 * A constructor is required, and must call the super [android.app.IntentService.IntentService]
 * constructor with a name for the worker thread.
 */
class HelloIntentService : IntentService("HelloIntentService") {

    /**
     * The IntentService calls this method from the default worker thread with
     * the intent that started the service. When this method returns, IntentService
     * stops the service, as appropriate.
     */
    override fun onHandleIntent(intent: Intent?) {
        // 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()
        }

    }
}

Java

public class HelloIntentService extends IntentService {

  /**
   * A constructor is required, and must call the super <code><a href="/reference/android/app/IntentService.html#IntentService(java.lang.String)">IntentService(String)</a></code>
   * constructor with a name for the worker thread.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // 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();
      }
  }
}

É tudo de que você precisa: um construtor e uma implementação de onHandleIntent().

Se você decidir modificar outros métodos de callback, como onCreate(), onStartCommand() ou onDestroy(), certifique-se de chamar a superimplementação para que o IntentService lide adequadamente com a vida útil do thread de trabalho.

Por exemplo, onStartCommand() precisa retornar a implementação padrão (que é como o intent é entregue para onHandleIntent()):

Kotlin

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

Java

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

Além de onHandleIntent(), o único método de que você não precisa chamar a superclasse é onBind(). Você só precisa implementá-lo se o serviço permitir vinculação.

Na próxima seção, você verá como o mesmo tipo de serviço é implementado ao estender a classe de base Service, que tem muito mais códigos, mas que pode ser apropriada para lidar com solicitações de inicialização simultâneas.

Extensão da classe Service

O uso de IntentService torna a implementação de um serviço iniciado muito simples. Se, no entanto, você precisar do serviço para realizar vários threads (em vez de processar as solicitações de inicialização por meio de uma fila de trabalho), será possível estender a classe Service para lidar com cada intent.

Para comparar, o código de exemplo a seguir mostra uma implementação da classe Service que realiza o mesmo trabalho do exemplo anterior usando IntentService. Ou seja, para cada solicitação de inicialização, ela usa um thread de trabalho para realizar o job e processa somente uma solicitação por vez.

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

É possível notar que é muito mais trabalhoso do que usar o IntentService.

No entanto, como você lida com cada chamada de onStartCommand(), é possível realizar várias solicitações simultaneamente. Isso não é o que esse exemplo faz. No entanto, se quiser, é possível criar um novo thread para cada solicitação e executá-las na hora, em vez de esperar que a solicitação anterior seja concluída.

O método onStartCommand() precisa retornar um número inteiro. O número inteiro é um valor que descreve como o sistema continuará o serviço no evento em que o sistema o eliminar. A implementação padrão para IntentService cuida disso para você, mas é possível modificá-la. O valor de retorno de onStartCommand() precisa ser uma das seguintes constantes:

START_NOT_STICKY
Se o sistema eliminar o serviço após o retorno de onStartCommand(), não recrie o serviço, a não ser que tenha intents pendentes para entregar. Essa é a opção mais segura para evitar executar o serviço quando desnecessário e quando o aplicativo puder simplesmente reiniciar qualquer job incompleto.
START_STICKY
Se o sistema eliminar o serviço após o retorno de onStartCommand(), recrie o serviço e chame onStartCommand(), mas não entregue novamente o último intent. Em vez disso, o sistema chama onStartCommand() com intent nulo, a não ser que tenha intents pendentes para iniciar o serviço. Nesse caso, o intents são entregues. Isso é adequado para reprodutores de mídia (ou serviços semelhantes) que não estejam executando comandos, mas estejam em execução indefinidamente e aguardando um job.
START_REDELIVER_INTENT
Se o sistema eliminar o serviço após o retorno de onStartCommand(), recrie o serviço e chame onStartCommand() com o último intent entregue ao serviço. Quaisquer intents pendentes são entregues um de cada vez. Isso é adequado para serviços que estejam realizando um job ativamente que deva ser retomado imediatamente, como o download de um arquivo.

Para mais informações sobre esses valores de retorno, veja a documentação de referência vinculada a cada constante. Para um exemplo estendido desse tipo de implementação de serviço, consulte a classe MessagingService na amostra de MessagingService no GitHub.

Como iniciar um serviço

É possível iniciar um serviço de uma atividade ou outro componente do aplicativo passando um Intent a startService() ou startForegroundService(). O sistema Android chama o método onStartCommand() do serviço e passa o Intent a ele, que especifica qual serviço iniciar.

Observação: se seu aplicativo é direcionado à API de nível 26 ou posterior, o sistema impõe restrições à execução de serviços em segundo plano quando o próprio aplicativo não estiver em primeiro plano. Caso um aplicativo precise criar um serviço em primeiro plano, ele chamará startForegroundService(). Esse método cria um serviço em segundo plano, mas sinaliza ao sistema que o serviço se promoverá ao primeiro plano. Ao ser criado, o serviço precisa chamar o método startForeground() em cinco segundos.

Por exemplo, uma atividade pode iniciar o serviço de exemplo na seção anterior (HelloService) usando um intent explícito com startService(), como pode ser visto aqui:

Kotlin

Intent(this, HelloService::class.java).also { intent ->
    startService(intent)
}

Java

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

O método startService() retorna imediatamente, e o sistema Android chama o método onStartCommand() do serviço. Se o serviço ainda não estiver em execução, o sistema primeiro chama onCreate() e, em seguida, chama onStartCommand().

Se o serviço não fornecer vinculação, o intent entregue com startService() será o único modo de comunicação entre o componente do aplicativo e o serviço. No entanto, se quiser que o serviço envie um resultado de volta, o cliente que inicia o serviço pode criar um PendingIntent para uma transmissão (com getBroadcast()) e entregá-lo ao serviço no Intent que inicia o serviço. O serviço pode então usar a transmissão para entregar um resultado.

Várias solicitações para iniciar o serviço resultam em diversas chamadas correspondentes ao onStartCommand() do serviço. No entanto, somente uma solicitação para interromper o serviço (com stopSelf() ou stopService()) é necessária para interrompê-lo.

Interrupção de um serviço

Um serviço iniciado precisa gerenciar o próprio ciclo de vida. Ou seja, o sistema não interrompe nem elimina o serviço, a não ser que tenha que recuperar a memória do sistema e o serviço continuar em execução depois que onStartCommand() retornar. O serviço precisa ser interrompido chamando stopSelf() ou outro componente que possa interrompê-lo chamando stopService().

Ao solicitar a interrupção com stopSelf() ou stopService(), o sistema elimina o serviço assim que possível.

No entanto, se o serviço lidar com várias solicitações para onStartCommand() ao mesmo tempo, não será recomendado interromper o serviço ao terminar o processamento de uma solicitação de inicialização, porque é possível que uma nova solicitação de inicialização tenha ocorrido (interromper no final da primeira solicitação eliminaria a segunda). Para evitar esse problema, é possível usar stopSelf(int) para garantir que a solicitação que interrompe o serviço sempre se baseie na solicitação de inicialização mais recente. Ou seja, ao chamar stopSelf(int), você passa o código da solicitação de inicialização (o startId entregue a onStartCommand()) para aquele a que a solicitação de interrupção corresponde. Em seguida, se o serviço receber uma nova solicitação de inicialização antes que você possa chamar stopSelf(int), o código não corresponderá e o serviço não será interrompido.

Atenção: é importante que um aplicativo interrompa os serviços quando terminar os trabalhos para evitar o desperdício dos recursos do sistema e consumo da bateria. Se necessário, outros componentes podem chamar stopService() para interromper o serviço. Mesmo que você ative a vinculação ao serviço, é sempre necessário interrompê-lo por conta própria se ele tiver recebido uma chamada de onStartCommand().

Para mais informações sobre o ciclo de vida de um serviço, consulte a seção Gerenciamento do ciclo de vida de um serviço abaixo.

Criação de um serviço vinculado

Serviços vinculados permitem que componentes de aplicativo sejam vinculados chamando bindService() para criar uma conexão de longo prazo. Eles geralmente não permitem que os componentes os iniciem chamando startService().

Deve-se criar um serviço vinculado quando se quer interagir com o serviço a partir de atividades e outros componentes no aplicativo ou para expor algumas das funcionalidades do aplicativo para outros aplicativos, por meio de comunicação entre processos (IPC).

Para criar um serviço vinculado, implemente o método de callback onBind() para retornar um IBinder que defina a interface de comunicação com o serviço. Outros componentes de aplicativo podem chamar bindService() para recuperar a interface e começar a chamar métodos no serviço. O serviço existe somente para disponibilizar o componente do aplicativo a que ele está vinculado. Portanto, quando não houver componentes vinculados ao serviço, o sistema o eliminará. Não é necessário interromper um serviço vinculado da mesma maneira que quando o serviço é iniciado por meio de onStartCommand().

Para criar um serviço vinculado, a primeira coisa a se fazer é especificar como um cliente pode se comunicar com o serviço. Essa interface entre o serviço e um cliente precisa ser uma implementação de IBinder e é o que o serviço precisa retornar do método de callback onBind(). Quando o cliente receber IBinder, ele poderá começar a interagir com o serviço por meio da interface.

Vários clientes podem se vincular ao serviço por vez. Quando um cliente terminar de interagir com o serviço, ele chamará unbindService() para se desvincular. Quando não houver clientes vinculados ao serviço, o sistema o eliminará.

Há várias maneiras de implementar um serviço vinculado e a implementação é mais complicada que um serviço iniciado. Logo, a discussão sobre serviços vinculados aparece em um documento separado sobre Serviços vinculados.

Envio de notificações ao usuário

Quando em execução, um serviço pode notificar o usuário sobre eventos usando Notificações de aviso ou Notificações da barra de status.

Uma notificação de aviso é uma mensagem que aparece na superfície da janela atual por um breve momento antes de desaparecer. Uma notificação da barra de status fornece um ícone na barra de status com uma mensagem que o usuário pode selecionar para realizar uma ação (como iniciar uma atividade).

Geralmente, uma notificação da barra de status é a melhor técnica quando um trabalho de segundo plano é concluído (como download de arquivo completo) e o usuário pode agir sobre ele. Quando o usuário seleciona a notificação a partir da visualização expandida, ela pode iniciar uma atividade (como a exibição do arquivo transferido).

Consulte os guias de desenvolvedor Notificações de aviso ou Notificações da barra de status para mais informações.

Execução de serviço em primeiro plano

Um serviço em primeiro plano é aquele com que o usuário está ativamente interagindo e não é uma opção para o sistema eliminá-lo quando a memória estiver baixa. Um serviço em primeiro plano precisa fornecer uma notificação para a barra de status, que é colocada sob o cabeçalho Ongoing. Isso significa que a notificação não pode ser dispensada, a não ser que o serviço seja interrompido ou removido do primeiro plano.

Atenção: limite o uso de serviços em primeiro plano no seu aplicativo.

É recomendado que você use serviços em primeiro plano somente quando o aplicativo precisar realizar uma tarefa que seja perceptível ao usuário, mesmo quando ele não estiver interagindo diretamente com o aplicativo. Por esse motivo, serviços em primeiro plano precisam exibir uma notificação de barra de status com uma prioridade de PRIORITY_LOW ou superior. Isso ajuda a garantir que o usuário perceba o que o aplicativo está fazendo. Se a ação for de importância tão baixa que você queira usar uma notificação de prioridade mínima, provavelmente você não deveria estar usando um serviço. Em vez disso, considere usar um job agendado.

Todo aplicativo que executa um serviço adiciona um carregamento ao sistema, o que consome os recursos. Se um aplicativo tentar esconder os serviços usando uma notificação de prioridade baixa, o desempenho do aplicativo com que o usuário está interagindo poderá diminuir. Por isso, se um aplicativo tentar executar um serviço com uma notificação de prioridade mínima, o sistema chamará o comportamento do aplicativo na notificação da seção inferior da gaveta.

Por exemplo, um reprodutor de música que reproduz a partir de um serviço precisa ser configurado para permanecer em execução em primeiro plano, porque o usuário está prestando atenção em sua operação explicitamente. A notificação na barra de status pode indicar a música atual e permitir que o usuário inicie uma atividade para interagir com o reprodutor de música. De modo semelhante, um aplicativo de acompanhamento de corridas do usuário precisa de um serviço em primeiro plano para rastrear a localização.

Para solicitar a execução do serviço em primeiro plano, chame startForeground(). Esse método usa dois parâmetros: um número inteiro que identifica de forma exclusiva a notificação e Notification para a barra de status. A notificação precisa ter uma prioridade de PRIORITY_LOW ou superior. Vejamos um exemplo:

Kotlin

val pendingIntent: PendingIntent =
        Intent(this, ExampleActivity::class.java).let { notificationIntent ->
            PendingIntent.getActivity(this, 0, notificationIntent, 0)
        }

val notification: Notification = Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
        .setContentTitle(getText(R.string.notification_title))
        .setContentText(getText(R.string.notification_message))
        .setSmallIcon(R.drawable.icon)
        .setContentIntent(pendingIntent)
        .setTicker(getText(R.string.ticker_text))
        .build()

startForeground(ONGOING_NOTIFICATION_ID, notification)

Java

Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent =
        PendingIntent.getActivity(this, 0, notificationIntent, 0);

Notification notification =
          new Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)
    .setContentTitle(getText(R.string.notification_title))
    .setContentText(getText(R.string.notification_message))
    .setSmallIcon(R.drawable.icon)
    .setContentIntent(pendingIntent)
    .setTicker(getText(R.string.ticker_text))
    .build();

startForeground(ONGOING_NOTIFICATION_ID, notification);

Atenção: o código de número inteiro que você fornece a startForeground() não pode ser igual a 0.

Para remover o serviço do primeiro plano, chame stopForeground(). Esse método usa um booleano indicando se removerá também a notificação da barra de status. Esse método não interrompe o serviço. No entanto, se você interromper o serviço enquanto estiver em execução em primeiro plano, a notificação também será removida.

Para mais informações sobre notificações, consulte Criação de notificações da barra de status.

Gerenciamento do ciclo de vida de um serviço

O ciclo de vida de um serviço é muito mais simples do que o de uma atividade. No entanto, é ainda mais importante que você preste muita atenção em como o serviço é criado e eliminado, pois ele pode ser executado em primeiro plano sem que o usuário esteja ciente.

O ciclo de vida do serviço — desde a criação à eliminação — pode seguir um destes dois caminhos:

  • Um serviço iniciado

    O serviço é criado quando outro componente chama startService(). Depois, o serviço permanece em execução indefinidamente e precisa chamar stopSelf() para ser interrompido. Outro componente também pode interromper o serviço chamando stopService(). Quando o serviço é interrompido, o sistema o elimina.

  • Um serviço vinculado

    O serviço é criado quando outro componente (um cliente) chama bindService(). O cliente se comunica com o serviço pela interface IBinder. O cliente pode escolher encerrar a conexão chamando unbindService(). Vários clientes podem ser vinculados ao mesmo serviço e, quando todos os vínculos terminam, o sistema elimina o serviço. O serviço não precisa ser interrompido.

Esses dois caminhos não são inteiramente separados. É possível vincular um serviço que já foi iniciado com startService(). Por exemplo, você pode iniciar um serviço de música em segundo plano iniciando startService() com um Intent que identifica a música a ser reproduzida. Depois, possivelmente quando o usuário quiser exercer mais controle sobre o reprodutor ou ter informações sobre a música em reprodução, uma atividade pode chamar bindService(). Nesses casos, stopService() ou stopSelf() não interrompem o serviço até todos os clientes serem desvinculados.

Implementação dos callbacks do ciclo de vida

Como uma atividade, um serviço tem um método de ciclo de vida que é possível implementar para monitorar as alterações no estado do serviço e realizar trabalhos em momentos adequados. O serviço básico a seguir demonstra cada um dos métodos de ciclo de vida:

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

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

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

    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 mStartMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return mBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return mAllowRebind;
    }
    @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
    }
}

Observação: diferentemente dos métodos de callback do ciclo de vida da atividade, você não precisa chamar a implementação da superclasse.

Figura 2. Ciclo de vida do serviço. O diagrama à esquerda mostra o ciclo de vida quando o serviço é criado com startService() e o diagrama à direita mostra o ciclo de vida quando o serviço é criado com bindService().

A figura 2 ilustra os métodos de callback tradicionais para um serviço. Apesar de a figura separar os serviços que são criados por startService() daqueles que são criados por bindService(), qualquer serviço, não importa como foi iniciado, pode permitir a vinculação. Um serviço que era antes iniciado com onStartCommand() (por um cliente que chamou startService()) ainda pode receber uma chamada para onBind() (quando um cliente chama bindService()).

Ao implementar esses métodos, é possível monitorar dois loops aninhados no ciclo de vida do serviço:

  • O ciclo de vida completo de um serviço ocorre entre o período em que onCreate() é chamado e o momento em que onDestroy() retorna. Como uma atividade, os serviços fazem sua configuração inicial em onCreate() e liberam todos os recursos restantes em onDestroy(). Por exemplo, um serviço de reprodução de música pode criar o thread em que a música é reproduzida em onCreate() e, em seguida, pode interromper o thread em onDestroy().

    Observação: os métodos onCreate() e onDestroy() são chamados para todos os serviços independentemente de terem sido criados por startService() ou bindService().

  • O ciclo de vida ativo de um serviço começa com a chamada a onStartCommand() ou a onBind(). Cada método entrega o Intent que foi passado para startService() ou bindService().

    Se o serviço for iniciado, o ciclo de vida ativo terminará no mesmo momento em que o ciclo de vida inteiro terminar (o serviço permanece ativo mesmo após o retorno de onStartCommand(). Se o serviço estiver vinculado, o ciclo de vida ativo acabará quando onUnbind() retornar.

Observação: apesar de um serviço iniciado ser interrompido com uma chamada de stopSelf() ou stopService(), não haverá nenhum callback respectivo para o serviço (não há callback de onStop()). Portanto, a não ser que o serviço esteja vinculado a um cliente, o sistema o eliminará quando for interrompido — onDestroy() será o único callback recebido.

Para mais informações sobre como criar um serviço que forneça vinculação, consulte o documento Serviços vinculados, que aborda mais profundamente o método de callback onRebind() na seção sobre Gerenciamento do ciclo de vida de um serviço vinculado.