quarta-feira, 7 de fevereiro de 2018

Service - Parte 1


Fala galera, hoje falaremos sobre serviços no android, mais especificamente da classe Service.


A classe Service é utilizada para executar um serviço em segundo plano, por vezes vinculado a algum processo que deve executar por tempo indetermidado e tem um alto consumo de recursos(Memória e CPU); Um serviço pode ser utilizado também para criar aplicações executadas em segundo plano sem a necessidade de exibir uma interface ao usuário.

Tipos de Serviço
Existem dois tipos de serviço:
  • Started (unbounded:ilimitado): são serviços iniciados por outros componentes (activities, broadcasts, etc.) através dos métodos startService(). Após iniciado o serviço pode continuar executando indefinidamente.
  • Bounded(limitado): são serviços iniciados através do método bindService(). Este tipo de serviço interage com outros componentes através de uma interface cliente-servidor, pode inclusive ocorrer entre diferentes processos (IPC-Interprocess Comunication). É executado enquanto possuir requisições a serem tratadas.

Cliclo de Vida
O ciclo de vida de um serviço é análogo ao de uma Activity, porém um pouco mais simples:





Iniciando e Parando um Serviço
Para iniciar um serviço é necessário, primeiramente, criar uma subclasse de android.app.Service e configurar o AndroidManifest.xml, como todas as outras classes do Android, usanto uma tag <sevice>:

<service android:name=".HelloService"/>

E para iniciar basta chamar o método startService(intent);

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

Se preferir disparar o serviço por uma ação utilize a tag <intente-filter>:

 <service android:name=".HelloService">

   <intent-filter>

    <action android:name="ACAO_PARA_INICIAR_O_SERVICE"/>

    <category android:name="android.intent.category.DEFAULT"/>

   </intent-filter>

 </service>


Então poderíamos iniciar o serviço com o seguinte código:

startService(new Intent(“ACAO_PARA_INICIAR_O_SERVICE”));

O método startService(intent) inicia um serviço que fica executando por tempo indetermidado e vai continuar executando mesmo se o usuário sair da aplicação, ou seja, o serviço é independente da activity. 
Depois de iniciado existem duas maneiras de parar um serviço, a primeira é chamar o método stopService(intent) com a mesma intent que foi utilizada para iniciá-lo. A segundo é o próprio serviço se encerrar com o método stopSelf().


Exemplo Prático
Agora vamos partir para prática e criar o projeto HelloService no Android Studio.
Como dito anteriormente para criar um serviço é preciso criar uma subclasse de android.app.Service e implementar obrigatóriamente o método IBinder onBind(intent). Podem ser também implementados de forma opcional os métodos onCreate(), onStartCommand(intent, flags, startId) e onDestroy() que fazem parte do ciclo de vida da classe Service.

HelloService.class

import com.codigosandroid.utils.utils.LogUtil;

import com.codigosandroid.utils.utils.NotificationUtil;

public class HelloService extends Service {
    private static final int MAX = 10;

    private static final String TAG = "codigos_android";

    protected int count;

    private boolean running;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        // null aqui porque não queremos interagir com o serviço(veremos um exemplo disso depois)
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        LogUtil.debug(TAG, "HelloService.onCreate() - Service criado");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.debug(TAG, "HelloService.onStartCommand() - Service iniciado: " + startId);
        count = 0;
        /* Método chamado depois do onCreate(), logo depois de iniciaro serviço
         * O parâmetro startId representa o identificador deste serviço */
        running = true;
        // Deleta para uma thread
        new WorkerThread().start();
        // Chama a implementação da superclasse
        return super.onStartCommand(intent, flags, startId);
    }

  // Thread que faz o trabalho pesado
  class WorkerThread extends Thread {
     @Override
      public void run() {
            try {
              while (running && count < MAX){
                  // Simula algum processamento
                  Thread.sleep(1000);
                  LogUtil.debug(TAG, "HelloService executando... " + count);
                  count++;
              }
              LogUtil.debug(TAG, "HelloService fim.");
          } catch (InterruptedException e) {
              LogUtil.error(TAG, e.getMessage(), e);
          } finally {
              // Auto-Encerra o serviço se o contador chegou a 10
              stopSelf();
              // Cria uma notificação para avisar o usuário que terminou.
              Context context = HelloService.this;
              Intent intent = new Intent(context, MainActivity.class);
             NotificationUtil.create(context, 1, intent, R.mipmap.ic_launcher, "HelloService", "Fim do  serviço.");
          }
     }
}

@Override
 public void onDestroy() {
       super.onDestroy();
       /* Ao encerrar o serviço, altere o flag para a thread parar (isto é importante para encerrar
       a thread caso alguém tenha chamado o stopService(intent) */
       running = false;
       LogUtil.debug(TAG, "HelloService.onDestroy() - Service destruído");
  }

}

Para o código compilar importe a classe NotificationUtil da biblioteca com.github.developermobile:utils:1.1.2. Feito isso configura o serviço no arquivo manifesto:

AndroidManifest.xml

<application..>
   <activity android:name=".MainActivity">
      <itent-filter>
         <action android:name="android.itent.action.MAIN">
         <category android:name="android.intent.category.LAUNCHER">
      </intent-filter>
   </activity>
   <service android:name=".HelloService"/>
</application>

Para mostrar como inciar o serviço vamos criar uma activity com os botões start e stop:
/res/layout/activity_main.xml:
<LinearLayou... android:orientation="vertical">

    <TextView

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="Exemplo de serviço, verifique os logs no LogCat." />



    <Button

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Start"

        android:onClick="onClickStart"/>



    <Button

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Stop"

        android:onClick="onClickStop"/>

</LinearLayout>

No código da activity implemente os métodos que tratam os eventos dos botões para iniciar e parar o serviço, note que a mesma intent utilizada para iniciar o serviço é usada para pará-lo.

MainActivity.class
public class MainActivity extends BaseActivity {

 public static final Class<? extends Service> CLS = HelloService.class;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onClickStart(View view) {
        startService(new Intent(this, CLS));
    }

    public void onClickStop(View view) {
        stopService(new Intent(this, CLS));
        NotificationUtil.cancell(this, 1);
    }

}


Feito isso excute o projeto e clique no botão Start para iniciar o serviço. O resultado da execução você pode conferir no LogCat que deve mostrar as seguintes mensagens:

02-07 13:16:25.142 4430-4430/com.codigosandroid.helloservice D/codigos_android: HelloService.onCreate() - Service criado
02-07 13:16:25.142 4430-4430/com.codigosandroid.helloservice D/codigos_android: HelloService.onStartCommand() - Service iniciado: 1
02-07 13:16:26.146 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 0
02-07 13:16:27.148 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 1
02-07 13:16:28.150 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 2
02-07 13:16:29.152 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 3
02-07 13:16:30.156 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 4
02-07 13:16:31.158 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 5
02-07 13:16:32.159 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 6
02-07 13:16:33.161 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 7
02-07 13:16:34.162 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 8
02-07 13:16:35.164 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService executando... 9
02-07 13:16:35.164 4430-4508/com.codigosandroid.helloservice D/codigos_android: HelloService fim.
02-07 13:16:35.170 4430-4430/com.codigosandroid.helloservice D/codigos_android: HelloService.onDestroy() - Service destruído

Observe que o próprio serviço encerrou seu processo chamando o método stopSelf();
Apesar de acompanhar a execução do serviço pelo LogCat, você pode ver na imagem abaixo a notificação criada quando o serviço termina por conta própria, ou seja, quando o contador chega a 9:

 

Deixar o serviço executando e depois sair de uma tela:
O que acontece com o exemplo que criamos se logo depois de iniciarmos o serviço o usuário sair da tela? A resposta é que a activity seria destruída, mas o serviço não. Ele continuaria executando em segundo plano automaticamente. Para pará-lo basta iniciar a activity novamente e clicar no botão Stop ou esperar que o serviço termine.

Nota: No projeto deste tópico foi utilizado uma dependência para a notificação que informa o fim do serviço, posteriormente, ao fim desta série sobre a classe Service farei um tópico sobre a classe que cria essas notificações:a classe Notification.
 
Link do repositório com os projetos do blog incluindo nosso exemplo de Service: