Operações e Tarefas no Android

android1Quando falamos de operações e tarefas em aplicativos, logo pensamos em processos, ou algo que é feito, ou alguma ação que a aplicação faz para o usuário.

No Android isso pode ser feito de diversas formas, e é um assunto delicado pois se não tivermos uma boa estratégia, o aplicativo pode sofrer problemas de performance e afetar diretamente o usuário. Por isso é importante pensar bem a forma que o processo será implementado. Dentre eles, temos Services, Threads, IntentServices e AsyncTask, mais abaixo será explicado cado um.

Por exemplo, em um aplicativo que está sendo criado, baixamos um conteúdo da internet, um arquivo com dados do tipo Json, esse aplicativo acessa o servidor, grava o arquivo localmente e fecha a conexão com o servidor. Feito isso, temos um arquivo local com dados a serem processados. Os dados vão ser visualizados em uma tela para o usuário, mas enquanto isso esse mesmo usuário está navegando pelo aplicativo, fazendo outras operações, ou até mesmo esperando essa tela carregar, mas a aplicação tem que ter algum tipo de feedback para que não tenha sensação de que o aplicativo travou. Dado a situação acima, nos perguntamos: Qual melhor estratégia para se processar o arquivo sem que atrapalhe o desempenho da aplicação e não interfira em seu uso?

Antes de prosseguirmos com uma solução para esse problema, vamos entender um pouco quais são as opções que o Android nos fornece.

Services

Os Services são indicados apenas quando necessários, pois como os Services ficam o tempo todo em execução em background, mesmo se o usuário não estiver usando a aplicação, a bateria pode ser drenada e também como são executados na Thread principal do Android, se por acaso o usuário estiver interagindo com uma Activity, que inclusive está na Thread principal também, pode levar a problemas de desempenhos na UI, deixando assim a aplicação lenta e com demora de resposta ao usuário [1].

Para implementar um serviço, é necessário inserir o mesmo dentro de application no manifesto Android, criar uma classe que estenda a classe Service e implementar dois de seus métodos.

Exemplo de como declarar o Service no manifesto Android:

<br />&lt;service android:enabled="true" android:name=".services.MyService" /&gt;<br />

Exemplo de uma classe que implementa o Service:

<br />public class MyService extends Service {<br /> @Override<br /> public int onStartCommand(Intent intent, int flags, int startId) {<br /> return Service.START_NOT_STICKY;<br /> }<br /><br /> @Override<br /> public IBinder onBind(Intent intent) {<br /> return null;<br /> }<br />}<br />
Listagem 1 – Exemplo de uma classe Service simples

Threads

Quando uma aplicação é iniciada, o Android cria aThread principal para esta aplicação, ou seja, todos os processos da aplicação são feitos dentro dessa Thread principal.

Para operações que não são instantâneas ao usuário, por exemplo, que possam congelar a tela durante o uso, é então necessário criar uma Thread separa chamada de Thread background ou worker thread. Mas para isso precisamos nos preocupar com a regra da Thread principal, que diz que não podemos acessar a toolkit de interface do Android de fora da UI Thread [2]. Para resolver isso existem métodos que nos ajudam a não quebrar essa regra na criação de um Thread:

  • Activity.runOnUiThread(Runnable)

  • View.post(Runnable)

  • View.postDelayed(Runnable, long)

Exemplo de como ficaria nossa Thread:

<br />public void onClick(View v) {<br /><br /> new Thread(new Runnable() {<br /> public void run() {<br /> final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");<br /> mImageView.post(new Runnable() {<br /> public void run() {<br /> mImageView.setImageBitmap(bitmap);<br /> }<br /> });<br /> }<br /> }).start();<br />}<br />
Listagem 2 – Atualizando um ImageView via Thread

Nesse exemplo estamos setando uma imagem no ImageView. Essa operação é realizada pela Thread separada, sendo que a ImageView é manipulada pela UI thread. Chamamos isso de implementação em modo Thread-safe.

IntentService

O IntentService é uma subclasse de Service. Mas diferente do Service que usa a Thread principal da aplicação, o IntentService usa o Worker Thread para executar seus serviços, através de uma fila que é criada, onde cada Intent é chamada sequencialmente sobre demanda. Quando o serviço é concluído, o IntentService é parado [3].

Podemos usar o IntentService, estendendo a classe IntentService e implementando o método onHandleIntent() para lidar com a requisição do Intent. A implementação ficaria conforme abaixo:

<br />public class HelloIntentService extends IntentService {<br /> /**<br /> * A constructor is required, and must call the super IntentService(String)<br /> * constructor with a name for the worker thread.<br /> */<br /> public HelloIntentService() {<br /> super("HelloIntentService");<br /> }<br /> /**<br /> * The IntentService calls this method from the default worker thread with<br /> * the intent that started the service. When this method returns, IntentService<br /> * stops the service, as appropriate.<br /> */<br /> @Override<br /> protected void onHandleIntent(Intent intent) {<br /> // Normally we would do some work here, like download a file.<br /> // For our sample, we just sleep for 5 seconds.<br /> long endTime = System.currentTimeMillis() + 5*1000;<br /> while (System.currentTimeMillis() &lt; endTime) {<br /> synchronized (this) {<br /> try {<br /> wait(endTime - System.currentTimeMillis());<br /> } catch (Exception e) {<br /> }<br /> }<br /> }<br /> }<br />}<br />
Listagem 3 – Exemplo de uma classe IntentService simples

AsyncTask

A AsyncTask faz operações em background e quando concluídas envia uma resposta para a UI Thread, sem a necessidade de manipular a Thread, pois ela se encarrega disso. Para isso ela faz operações assíncronas na interface com o usuário, bloqueando em Worker Thread o uso da aplicação.

Para usar o AsyncTask, basta criar uma classe que estende a classe AsyncTask<> e implemente o método doInBackground(), onde as operações serão executadas, o método onPostExecute(), que após executar a operação em background, é chamado para atualizar a UI. A chamada do AsyncTask é feita pelo método execute() [4]. Exemplos de como ficaria:

<br />public void onClick(View v) {<br /> new DownloadImageTask().execute("http://example.com/image.png");<br />}<br />
Listagem 4 – Execução de uma AsyncTask
<br />private class DownloadImageTask extends AsyncTask&lt;String, Void, Bitmap&gt; {<br /> /** The system calls this to perform work in a worker thread and<br /> * delivers it the parameters given to AsyncTask.execute() */<br /> protected Bitmap doInBackground(String... urls) {<br /> return loadImageFromNetwork(urls[0]);<br /> }<br /><br /> /** The system calls this to perform work in the UI thread and delivers<br /> * the result from doInBackground() */<br /> protected void onPostExecute(Bitmap result) {<br /> mImageView.setImageBitmap(result);<br /> }<br />}<br />
Listagem 5 – Utilizando AsysncTask para alterar um ImageView

Abaixo uma tabela para simplificar os componentes citados:

Service Thread IntentService AsyncTask
When to use ? Task with no UI, but shouldn’t be too long. Use threads within service for long tasks. – Long task in general. – For tasks in parallel use Multiple threads (traditional mechanisms) – Long task usually with no communication to main thread. – If communication is required, can use main thread handler or broadcast intents – When callbacks are needed (Intent triggered tasks). – Long task having to communicate with main thread. – For tasks in parallel use multiple instances OR Executor
Trigger Call to method onStartService() Thread start() method Intent Call to method execute()
Triggered From (thread) Any thread Any Thread Main Thread (Intent is received on main thread and then worker thread is spawed) Main Thread
Runs On (thread) Main Thread Its own thread Separate worker thread Worker thread. However, Main thread methods may be invoked in between to publish progress.
Limitations / Drawbacks May block main thread – Manual thread management – Code may become difficult to read – Cannot run tasks in parallel. – Multiple intents are queued on the same worker thread. – one instance can only be executed once (hence cannot run in a loop) – Must be created and executed from the Main thread
Tabela 1 – Tabela simplificada dos tipos de operações e tarefas do Android [5].

Agora que sabemos as opções que temos, vamos voltar ao nosso problema anterior. Qual melhor estratégia para se processar o arquivo sem que atrapalhe o desempenho da aplicação e não interfira em seu uso?

Bom, se o usuário estiver abrindo uma tela que visualiza os dados desse arquivo, ou seja, onde o aplicativo conecta ao servidor, baixa o arquivo, fecha conexão, processa e renderiza os dados na tela, então a melhor opção que temos é usando o AsyncTask, pois com ele será possível fazer todo esse processo em background, ele mesmo cuidará do gerenciamento da Thread, e para o usuário, será mostrado um alerta de processo em execução. Quando o processo terminar, o alerta será fechado e a tela renderizada com os dados.

Referências

[1] http://developer.android.com/guide/components/services.html

[2]  http://developer.android.com/guide/components/processes-and-threads.html#Threads

[3] http://developer.android.com/reference/android/app/IntentService.html

[4] http://developer.android.com/reference/android/os/AsyncTask.html

[5] http://techtej.blogspot.com.es/2011/03/android-thread-constructspart-4.html

Links Externos

Json

Por MATERA SYSTEMS

Postado em: 04 de julho de 2014

Confira outros artigos do nosso blog

[Webinar] Profile de aplicações Java com Oracle Mission Control e Flight Recorder

24 de julho de 2017

Danival Calegari

Criando Mocks de serviços REST com SoapUI

27 de junho de 2017

Monise Costa

Three laws that enable agile software development

09 de março de 2017

Celso Gonçalves Junior

Medindo performance de uma API REST

21 de fevereiro de 2017

Monise Costa

Deixe seu comentário