Desenvolvimento para Android Aula 7 - Services Prof. Markus Endler Services Um service é um tipo de componente de uma aplicação Android que é utilizado para executar operações de longa duração em background Services não possuem interface gráfica para o usuário São utilizados para operações de rede, processamento intenso, tocar músicas, etc. • • Por exemplo, em uma aplicação que toca músicas, uma música deve continuar a ser tocada para o usuário independente do que o mesmo esteja fazendo no dispositivo A comunicação pela rede pode demorar, e por isso é importante que ocorra de forma assíncrona (sem bloquear a app) Podem executar de forma independente do componente que os criou • Se uma activity inicia um service e esta é fechada, o service continuará executando em background Podem ser conectados a quaisquer outros componentes de aplicação (de acordo com as permissões) 2 Por exemplo, é possível permitir que qualquer aplicação possa conectar-se ao service e manipulá-lo Services – Formas Um service pode assumir duas formas: • Não-conectado (unbounded) • Um service é criado quando outro componente chama(*) o método startService(), pasando um Intent • O service executa indefinidamente (mesmo que o componente criador termine) e pode se parar chamando o método stopSelf(). • Um outro componente pode parar o service diretamente através do chamado stopService(). • Quando o service é parado, o sistema destrói o mesmo. • Conectado (bounded) • Um service é criado quando outro componente chama o método bindService(). • O componente (cliente) comunica-se com o service pela interface IBinder – podendo fazer comunicação entre diferentes processos. • O cliente pode fechar a conexão com o mesmo através da chamada unbindService(). • Múltiplos clientes podem se conectar ao mesmo service e quando todos estes fecham a conexão, o sistema Android destrói o service. (*) Em Android, “chamar” significa criar um Intent corresponente. 3 Services – Formas e Ciclo-de-vida 4 Services – Ciclo-de-vida public class CallbackLifeCycleExampleService extends Service { int mStartMode; // indica como se comportar caso o service seja destruído IBinder mBinder; // interface para clientes que fazem a conexão com o service boolean mAllowRebind; // indica se onRebind deve ser utilizado 5 @Override public void onCreate() { // O service está sendo criado } @Override public int onStartCommand(Intent intent, int flags, int startId) { // O service é iniciando devido a uma chamada a startService() return mStartMode; } @Override public IBinder onBind(Intent intent) { // Um cliente se conectou ao service usando bindService() return mBinder; } @Override public boolean onUnbind(Intent intent) { // Todos os clientes se desconectaram do service usando unbindService() return mAllowRebind; } @Override public void onRebind(Intent intent) { // Um cliente está se re-conectando ao service com bindService(), // depois de onUnbind() ter sido chamado } @Override public void onDestroy() { // O service não está sendo mais usado e está sendo destruído } } Services – Informações Um mesmo service pode funcionar de ambas as formas, bastando que implemente: • O método onStartCommand() para permitir que componentes o iniciem • O método onBind() para permitir que componentes se conectem ao mesmo Qualquer componente pode utilizar o service, mesmo aqueles que estejam em aplicações separadas • Da mesma forma como acontece com uma activity, utiliza-se um intent para ativar um service • Se a declaracão do Service no Manifest contiver um Intent filter, isso significa que qualquer componente (de outra app) pode ativa-lo com um implicit intent. Senão, somente pode ser invocado pelo seu nome da classe, através de um intent explicito. Isso é o default do atributo android:exported =[“true” |”false”] IMPORTANTE: um service executa normalmente na thread principal do processo que o abriga. • Se é necessário executa-lo em um processo separado, deve-se explicitar isto em isolatedProcess e dar o nome em process na declaracão no manifesto 6 • Dependendo da utilização do service, deve-se criar uma thread separada para Criação de um service Para desenvolver um service deve-se estender a classe android.app.Service • É preciso implementar métodos para atender o ciclo-de-vida e fornecer mecanismos para que seja possível se conectar ao service • Obs: em situacões de escassez de memória, o Android também pode destruir um service. Para que estado do service possa ser recriado posteriormente, os métodos do ciclo de vida precisam ser implementados. Os mais importantes métodos são: • onStartCommand(): método chamado quando um outro componente solicita que o service seja iniciado utilizado startService() • onBind(): método chamado quando um outro componente solicita se conectar ao service através do método bindService() • É necessário fornecer uma interface, Ibinder, para os clientes se comunicarem com o service • onCreate(): método para procedimentos de configuração inicial do service, chamado quando o service é criado (antes de chamar onStartCommand() ou onBind() • onDestroy(): método chamado quando o service não está mais em uso e deve ser destruído – aqui devemos liberar recursos alocados (p. ex. threads) 7 public class HelloService extends Service { Extensão da android.app.Service private static final String TAG = HelloService.class.getName(); @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "Service iniciou"); return super.onStartCommand(intent, flags, startId); } Exemplo de service @Override public void onCreate() { Log.d(TAG, "Service parou"); super.onCreate(); } 8 @Override public void onDestroy() { Log.d(TAG, "Service destruído"); super.onDestroy(); } Método onStartCommand() é chamado quando algum componente executa o método startService() Método onBind() deve retornar interface para chamadas remotas ao service – neste exemplo, não é possível se conectar a este service @Override public IBinder onBind(Intent arg0) { // Retorna nulo quer dizer que clientes não podem se conectar ao service return null; } } Declaração de um service Como os outros componentes Android – activities, content providers e broadcast receivers – um service deve ser declarado no arquivo de manifesto da aplicação para que este seja usado pela aplicação -O único atributo obrigatório é o nome da classe que estende android.app.Service <application> ... <service android:name="HelloService" android:process=":the_process" android:exported="true” android:permission=“abc”> </service> ... </application> 9 -O atributo android:process determina o processo que executará este service, podendo ser o mesmo da aplicação, privado à aplicação (definido no atributo process de application), ou em um processo independente, sendo acessível e compartilhado para outras aplicações (quando começa com ‘:’) -O atributo android:exported determina se este service é visível para outras aplicações através de intent filters - Atributo android:permission define a permissão necesearia para ativar o service IntentService IntentService é uma subclasse de android.app.Service que facilita o desenvolvimento de services e que utiliza uma única thread para tratar todas as requisições para iniciar o service, uma por vez • Esta é a melhor opção se o service não precisa tratar requisições concorrentes/simultâneas • Basta implementar onHandleIntent() que recebe o intent de cada solicitação de ativação do service A maioria dos services desenvolvidos não tem o requisito de tratar requisições concorrentes • Nestes casos, é muito mais fácil utilizar o IntentService como subclasse para implementação do service 10 Iniciando um service Deve-se sempre iniciar um service utilizando intents com o método startService() O Android chamará o método startCommand() passando como parâmetro o intent utilizado Intent intent = new Intent(this, HelloService.class); startService(intent); • Múltiplas requisições para iniciar o service vão chamar múltiplas vezes o método onStartCommand() • Quando um service não permite a conexão, a única forma de passar dados para o service é através do intent utilizado 11 Parando um service Para parar um service, o próprio deve chamar o método stopSelf() ou algum componente deve chamar o método stopService() Intent intent = new Intent(this, HelloService.class); stopService(intent); • Quando estes métodos – stopSelf() e stopService() – são chamados, o Android imediatamente destrói o service • Contudo, se o service deve receber múltiplas requisições concorrentes para executar tarefas independentes , deve-se utilizar o identificador de cada requisição para controlar quando o service será destruído • Atenção: É importante sempre parar o service quando seu trabalho foi finalizado para liberar recursos e não consumir bateria 12 Habilidade p/ criar threads public class MyService extends Service implements Runnable{ Exemplo de service com thread private static final String TAG = HelloService.class.getName(); private boolean ativo; 13 @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "Service iniciou"); return super.onStartCommand(intent, flags, startId); } @Override public void onCreate() { Log.d(TAG, "Service foi criado"); super.onCreate(); ativo = true; new Thead(this).start(); Criando uma nova thread } @Override public void onDestroy() { Log.d(TAG, "Service destruído"); super.onDestroy(); ativo = false; } public void run() { while (ativo && count < MAX) { fazAlgumaCoisa(); Log.d(TAG, ”processando..."); count++; } Log.d(TAG, ”terminei"); stopSelf(); } Veja: java.lang.Runnable#run() A therad executa um loop chamando fazAlgumaCoisa(); Executando em foreground Um service pode executar em foreground quando é considerado importante para o usuário, e não deva ser termiando quando a memória estiver baixa Para isto, é necessário chamar no próprio service o método startForeground(), que provê para o usuário uma notificação na barra de status • Esta notificação sumirá somente quando o service for parado ou tirado do foreground Notification notification = new Notification(R.drawable.icon, "Notif.", System.currentTimeMillis()); Intent notificationIntent = new Intent(this, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); notification.setLatestEventInfo(this, ”MonitorService", "Notif.", pendingIntent); startForeground(0, notification); Para tirar o service do foreground deve-se executar o método stopForeground() 14 Handler e Messenger O service pode se comunicar de volta com a activity através de um objeto Messenger • Este objeto é enviado para o service através do intent • Caso este Messenger esteja conectado com um Handler na activity, o service pode enviar objetos Message para a mesma public class MainActivity extends Activity { private Handler handler = new Handler() { @Override public void handleMessage(Message message) { ... //tratamento ao objeto Message enviado }; }; } 15 public void onClick(View view) { Intent intent = new Intent(this, DownloadService.class); Messenger messenger = new Messenger(handler); intent.putExtra("MESSENGER", messenger); startService(intent); Um novo objeto Messenger é passado } como extra do intent Handler e Messenger Para receber e processar a Messsage IntentService deve implementar o método onHandleIntent() public class DownloadService extends IntentService { ... @Override protected void onHandleIntent(Intent intent) { ... //Execução do service ... Bundle extras = intent.getExtras(); if (extras != null) { Messenger messenger = (Messenger) extras.get("MESSENGER"); Message msg = Message.obtain(); Método estático Message.obtain() msg.arg1 = 0; retorna um novo objeto Message msg.arg2 = 1; msg.obj = new Object(); msg.setData(extras); O objeto Message contém atributos e try { métodos para definir dados resultantes messenger.send(msg); do service } catch (android.os.RemoteException e1) { ... } } } 16 } Executando um service após boot Existem várias maneiras de agendar um service para execução • Pode ser interessante agendar a execução de um service após a inicialização do Android (o boot) • Para isto, basta criar um Broadcast Receiver que receba a notificação de que a inicialização completou, e que irá então iniciar o service <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <service android:name="HelloService" android:exported="true" android:process=":the_process"> </service> <receiver android:name="MyScheduleReceiver" > <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> 17 Executando um service após boot O BroadcastReceiver é notificado de que o Android finalizou seu boot e solicita o início da execução do service public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent service = new Intent(context, HelloService.class); context.startService(service); } } 18 Exemplo de Activity que Interage com Service usando Message Quando o serviço é executado, ele incrementa o número na frequência 10Hz. Quando a Activity faz um bind com o Service, irá exibir o valor atual. É possive também enviar mensagens para o Service para alterar o inclremento. Os dados (integer e string) são transferidos usando Message e Handlers. Estudar o código em https://bitbucket.org/alexfu/androidserviceexample/src 19 Handler na Activity final Messenger mMessenger = new Messenger(new IncomingHandler()); class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case MyService.MSG_SET_INT_VALUE: textIntValue.setText("Int Message: " + msg.arg1); break; case MyService.MSG_SET_STRING_VALUE: String str1 = msg.getData().getString("str1"); textStrValue.setText("Str Message: " + str1); break; default: super.handleMessage(msg); } } } 20 Obs: o Service é a classe Myservice O service estende a classe Handler, que implementa o médodo handleMessage, Message é recebida no Intent do onBind()