200 likes | 322 Views
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
E N D
Desenvolvimento para AndroidAula 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) • 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.
publicclassCallbackLifeCycleExampleService extendsService{int mStartMode;// indica como se comportar caso o service seja destruídoIBinder mBinder;// interface para clientes que fazem a conexão com o service boolean mAllowRebind;// indica se onRebind deve ser utilizado@OverridepublicvoidonCreate(){// O service está sendo criado}@OverridepublicintonStartCommand(Intent intent,int flags,int startId){// O service é iniciando devido a uma chamada a startService()returnmStartMode;}@OverridepublicIBinderonBind(Intent intent){// Um cliente se conectou ao service usando bindService()returnmBinder;}@OverridepublicbooleanonUnbind(Intent intent){// Todos os clientes se desconectaram do service usando unbindService()returnmAllowRebind;}@OverridepublicvoidonRebind(Intent intent){// Um cliente está se re-conectando ao service com bindService(),// depois de onUnbind() ter sido chamado}@OverridepublicvoidonDestroy(){// O service não está sendo mais usado e está sendo destruído}} Services – Ciclo-de-vida
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 • Dependendo da utilização do service, deve-se criar uma thread separada para executar suas operações intensivas em recursos e/ou bloqueantes
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)
Extensão da android.app.Service publicclass HelloService extends Service { privatestaticfinal String TAG = HelloService.class.getName(); @Override publicint onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "Service iniciou"); returnsuper.onStartCommand(intent, flags, startId); } @Override publicvoid onCreate() { Log.d(TAG, "Service parou"); super.onCreate(); } @Override publicvoid onDestroy() { Log.d(TAG, "Service destruído"); super.onDestroy(); } @Override public IBinder onBind(Intent arg0) { // Retorna nulo quer dizer que clientes não podem se conectar ao service returnnull; } } Exemplo de service 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
Declaração de um service -O único atributo obrigatório é o nome da classe que estende android.app.Service -O atributo android:process determina o processo que executará este service, podendo ser o mesmo da aplicação, privado à aplicação (definido no atributo processde application), ou em um processo independente, sendo acessível e compartilhado para outras aplicações (quando começa com ‘:’) -O atributo android:exporteddetermina se este serviceé visível para outras aplicações através de intentfilters - Atributo android:permissiondefine a permissão necesearia para ativar o 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 <application> ... <service android:name="HelloService" android:process=":the_process" android:exported="true” android:permission=“abc”> </service> ... </application>
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
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 • 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 Intent intent = new Intent(this, HelloService.class); startService(intent);
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() • 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 Intent intent = new Intent(this, HelloService.class); stopService(intent);
Habilidade p/ criar threads publicclass MyService extends Service implements Runnable{ privatestaticfinal String TAG = HelloService.class.getName(); private boolean ativo; @Override publicint onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "Service iniciou"); returnsuper.onStartCommand(intent, flags, startId); } @Override publicvoid onCreate() { Log.d(TAG, "Service foi criado"); super.onCreate(); ativo = true; new Thead(this).start(); } @Override publicvoid 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(); } } Exemplo de service com thread Criando uma nova thread 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 • Para tirar o service do foreground deve-se executar o método stopForeground() 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);
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 publicclass MainActivity extends Activity { private Handler handler = new Handler() { @Override publicvoid handleMessage(Message message) { ... //tratamento ao objeto Message enviado }; }; publicvoid 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
Para receber e processar a Messsage IntentService deve implementar o método onHandleIntent() Handler e Messenger publicclassDownloadServiceextendsIntentService { ... @OverrideprotectedvoidonHandleIntent(Intentintent) { ... //Execução do service ... Bundle extras = intent.getExtras(); if (extras != null) { Messenger messenger = (Messenger) extras.get("MESSENGER"); Messagemsg = Message.obtain(); msg.arg1 = 0; msg.arg2 = 1; msg.obj = newObject(); msg.setData(extras); try { messenger.send(msg); } catch (android.os.RemoteException e1) { ... } } } } Método estático Message.obtain() retorna um novo objeto Message O objeto Message contém atributos e métodos para definir dados resultantes do service
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>
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 publicclass MyReceiver extends BroadcastReceiver { @Override publicvoid onReceive(Context context, Intent intent) { Intent service = new Intent(context, HelloService.class); context.startService(service); } }
Exemplo de Activity que Interage com Serviceusando 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
Handler na Activity Obs: o Service é a classeMyservice 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); } } } • O service estende a classe Handler, queimplementaomédodohandleMessage, • Message érecebida no Intent do onBind()