Criando um Started Service
Um started service é um que outro componente inicia chamando startService(), resultando numa chamada para o método onStartCommand() do serviço.
Quando um serviço é iniciado, ele tem um ciclo de vida que é independente da do componente que o iniciou e o serviço pode rodar em background indefinidamente, mesmo que o componente que o iniciou seja destruído. Como tal, o serviço deverá ser parado apenas se o seu trabalho está concluído chamando stopSelf() ou outro componente pode parálo chamando stopService().
Um componente de aplicação como uma atividade pode iniciar o serviço chamando startService() e passando um Intent que especifica o serviço e inclui quaisquer dados para o serviço usar. O serviço recebe esse Intent no onStartCommand().
Suponha que uma atividade precise salvar dados em um banco de dados online. A atividade pode iniciar um serviço e entregar dados para salvar passando o intent para o startService(). O serviço recebe o intent no onStartCommand(), conecta-se à internet e faz a transação com o banco de dados. Quando a transação está realizada, o serviço pára e depois se destrói.
Tradicionalmente, existem duas classes que você pode extender para criar um started service.
Service
É a classe base para todos os serviços. Quando você extende essa classe é importante que você crie uma nova thread na qual fará todo o trabalho do service já que o serviço usa a thread principal de sua aplicação por default e que poderia diminuir sobremaneira a performance de quaisquer atividades que sua aplicação esteja rodando.
IntentService
É a subclasse de Service que usa um worker thread para manusear todos as requisições iniciais, um de cada vez. Essa é a melhor opção se você não quer que o seu serviço manuseie multiplas requisições simultâneamente. Todo que você precisa fazer é implementar onHandleIntent() que recebe o intent para cada requisição inicial para que você possa fazer o trabalho de background.
Extendendo a classe IntentService
Como muitos dos serviços iniciados não precisam manusear multiplas requisições simultaneamente (o que poderia ser muito perigoso para um cenário multi-threading), é provavelmente melhor que você implemente seu serviço usando a classe IntentService.
Um IntentService faz o seguinte:
- Cria uma classe padrão worker thread que executa todos os intents recebidos no onStartCommand() separado da thread principal da aplicação.
- Cria uma fila de trabalhos que passam um intent a cada vez para a sua implementação de onHandleIntent() para que você nunca precise se preocupar com multi-threading.
- Pára o serviço após todos as requisições iniciais terem sido processadas para que você nunca precise chamada stopSelf().
- Prove a implementação padrão de onBind() que retorna null
- Prove uma implementação padrão de onStartCommand() que envia o intent para a fila de trabalhos e então para sua implementação de onHandleIntent().
Aqui está um exemplo de implementação de IntentService:
public class HelloIntentService extends IntentService {
/**
* A constructor is required, and must call the super IntentService(String)
* 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.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
Isso é tudo o que é necessário: um construtor e uma implementação de onHandleIntent().
Se você decidir também por fazer um override de outro método callback como onCreate(), onStartCommand() ou onDestroy(), esteja certo de chamar a super implementação para que o IntentService possa processar corretamente a vida do worker thread.
Por exemplo, o onStartCommand() deve retornar para a implementação padrão (que é como o intent é entregue para o onHandleIntent()):
@OverrideAlém de onHandleIntent(), o único método a partir do qual você não precisará chamar a super classe é o onBind() (mas você precisará apenas implementar isso se o seu serviço permitir binding).
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent,flags,startId);
}
Extendendo a classe Service
Como visto na seção anterior, usar o IntentService faz sua implementação de um started service muito simples. Mas e se você quiser que seu serviço faça uso de multi-threading(ao invés de processar requisições iniciais através de uma fila de trabalho), então você pode extender a classe Service para manusear cada intent.
Para efeito de comparação, o exemplo seguinte é uma implementação de uma classe Service que realiza o trabalho exato como o do exemplo acima que usou o IntentService. Ou seja, para cada requisição inicial, ele usa um worker thread para realizar o trabalho e processar apenas uma requisição a cada vez.
public class HelloService extends Service {Como pode ver, acima há muito mais código do que havia quando usamos o IntentService.
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// 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.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// 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 will not 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
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@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 = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.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();
}
}
Mas como você tem de manusear cada chamada para onStartCommand() você mesmo, você pode realizar multiplas requisições simultâneamente. Não é o que o exemplo faz mas se é o que você quer, então você pode criar uma nova thread para cada requisição e rodá-los separadamente (ao invés de esperar a thread anterior terminar para a sua iniciar).
Note que o onStartCommand() deve retornar um inteiro. Um inteiro é um valor que descreve como o sistema deve continuar o serviço no evento deste matá-lo. O valor retornado de onStartCommand() deve ser uma das seguintes constantes:
START_NOT_STICKY
START_STICKY
START_REDELIVER_INTENT
Iniciando um serviço
Você pode iniciar um serviço a partir de uma atividade ou outro componente de aplicação passando um Intent para o startService(). O sistema Android chama o método onStartCommand() do serviço e passa-o para o Intent. Você nunca deverá chamar onStartCommand() diretamente. Deixe o sistema Android fazê-lo por você.
Por exemplo, uma atividade pode iniciar o serviço como abaixo:
Intent intent = new Intent(this, HelloService.class);
startService(intent);
O método startService retorna imediaamente e o sistema Android chama o método onStartCommand() do serviço. Se o serviço não está sendo rodado, o sistema primeiro chama onCreate() e então onStartCommand().
Se o serviço não prove binding, o intent entregue com o startService() é a única maneira de comunicação entre o componente de aplicação e o serviço. Contudo, se você quer que o serviço envie um resultado de volta, então o cliente que inicia o serviço pode criar um PendingIntent para um broadcast (com getBroadcast()) e entregá-lo ao serviço no Intent que inicia o serviço. O serviço pode então usar o broadcast para entregar o resultado.
Multiplas requisições para iniciar um serviço resultam em multiplas chamadas correspondentes para o onStartCommand() do serviço. Mas, apenas uma requisição para parar o serviço (com stopSelf() ou stopService()) é requerida.
Parando um serviço
Um started service deve gerenciar seu ciclo de vida. Ou seja, o sistema não pára ou destrói o serviço a menos que ele precise recuperar memória. Então, o serviço deve parar a si mesmo chamando stopSelf() ou outro componente pode pará-lo chamando stopService().
Uma vez que seja requisitado a parada com o stopSelf() ou stopService(), o sistema destrói o serviço assim que possível.
0 comentários:
Postar um comentário