Tutorial Android Executor – Parte 1

On 26 de junho de 2015 by admin

Introdução ao Android Executor

Hoje em dia, CPUs com vários núcleos tornaram-se quase a norma em dispositivos Android. Isso significa várias operações não tem de esperar para ser executadas em uma única Thread. Podem ser executados em várias threads em paralelo, onde cada thread é executada em um único processador. Por exemplo, imagine uma operação em que várias imagens têm de ser baixadas e mostradas em uma GalleryView. Os downloads e  decodificações necessárias para diferentes imagens poderiam acontecer simultaneamente em múltiplas threads agilizando toda a operação, levando a uma  app mais rápida. O Android Executor é a solução.

Este post faz parte de uma série de posts que abordam o paralelismo no Android. Para os outros no tema, consulte:

1 – Tutorial Android – Paralelismo: Introdução

2- Tutorial Android: Paralelismo – Threads

Neste post abordaremos o  framework Executor. Com este framework você pode gerenciar automaticamente um pool de threads com uma fila de tarefas. Múltiplas threads serão executadas em paralelo a partir de uma fila.  A interface Executor é o componente do Framework Android Executor[1]. Seu objetivo é permitir a gerência de uma thread pool, com paralelismo de maior independência entre as threads, mas mantendo um melhor controle sobre a execução das mesmas. 

Essa interface tem apenas um método, execute (), que executa o comando passado para ele em uma nova thread ou um thread pool ou no thread de chamada, dependendo da aplicação. O método execute () aceita um Runnable.

 

 

Captura de Tela 2015-06-25 às 22.49.50

Ele pode ser implementado e usado para executar um comando em vez de invocar uma nova thread with New Thread(nova RunnableTask ()). Start (). Como você vai ver, tudo que você tem a fazer é, implementar Executor e, em seguida, execute o comando Runnable em seu método execute () ou no mesmo segmento ou um novo segmento. Runnables executoras são tão fáceis como fazer isso:

 

[code language=”java” collapse=”false”]Executor executor = new MyExecutor();
executor.execute(new RunnableTarefaUm());
executor.execute(new RunnableTarefaDois());
[/code]

O controle das execuções pode ser feito atraves do método scheduleNext(). O exemplo abaixo implementa um Executor que executa uma fila de Runnables em série, com um modelo produtor/consumidor.

[code language=”java” collapse=”false”]class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque();
final Executor executor;
Runnable active;
SerialExecutor(Executor executor) {
this.executor = executor;
}
public synchronized void execute(final Runnable r) {
tasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}
[/code]

 

ExecutorService

ExecutorService é uma interface que estende Executor. Ele fornece métodos para gerenciar a execução de tarefas e o termino do pool de threads. Ele também fornece-nos com métodos como submit () que produz um futuro que pode ser usado para verificar se uma tarefa assíncrona foi concluída, espere a sua conclusão e recuperar o resultado retornado da operação.

 

Captura de Tela 2015-06-25 às 23.21.37

Vida Útil
Um Executor tem um ciclo de vida que é gerido pela  interface ExecutorService e implementado por ThreadPoolExecutor. Ele possui diferentes estados: Ready – Quando o Executor é criado e está pronto para aceitar tarefas para execução.

Running – Uma vez que uma tarefa é enviada para a execução, o executor se move para este estado, onde ele aceita tarefas de entrada e executa-los em um segmento de trabalho no pool de threads.
Shutdown – Estado após ExecutorService.shutdown () é chamado.
Stop – O estado após ExecutorService.shutdownNow () é chamado.
Clean
A limpeza interna. Este é o estado quando ambos fila de tarefas e pool de threads estão vazios.
Finished
– estado final quando o método terminated () foi concluída. Segmentos de espera (bloqueados) em ExecutorService.awaitTermination () também retornará

 

Uma vez que um estado é alcançado, ele não pode reverter para a anterior, ou seja, os estados não são reversíveis. É importante entender a diferença entre shutdown () e shutdownNow (). O primeiro método irá rejeitar novas tarefas, mas permitir que as tarefas previamente submetidos a processar antes de terminar. Por outro lado shutdownNow () interrompe segmentos de trabalho atuais para parar atuais tarefas de execução, bem como impede tarefas previamente submetidos a partida por removê-los da fila. Ele vai voltar a lista de Runnables pendentes que não foram executados de modo que você pode executá-los mais tarde em outras threads.

 

O fato de que os estados são irreversíveis também significa que uma vez desligamento foi iniciado, um Executor (pool de threads) não pode ser reutilizado para processar tarefas. Assim, um novo pool de threads tem de ser criado.Se um pool de threads não é manualmente desligamento, em seguida, uma vez que não há nenhum tópicos restantes dentro dela (manter vivo tempo decorrido) e não é referenciada pelo aplicativo mais, então ele vai desligar automaticamente / terminar.
 Thread Pools

 

Captura de Tela 2015-06-25 às 23.53.06
Casos de Uso do Executor
O uso do Executor pode ser útil nos seguintes casos:
  • Executar tarefas concorrentes que não tenham comunicação entre si
  • Múltiplas requisições http
  • Processamento de Imagens em paralelo
  • Códigos que necessitam de ganho de desempenho pela execução paralela
  • Utilizar ao Máximo o Hardware disponível

Em comparação com um Handler, a classe Executor é mais poderosa e pode usar um pool de threads, enquanto cada Handler faz referência a uma única Thread. O Executor permite que você obtenha todas as tarefas agendadas e cancelá-las, se for necessário. O Handler por outro lado não vai responder a perguntas simples, tais como, quantas tarefas estão esperando ou dar-me uma referência a todas as tarefas de espera.

Em geral, se você precisa de um pool de threads ou muito processamento, use o o Executor. Se você só precisa de uma thread de background simples para executar uma tarefa de cada vez use um Handler. Como exemplo, quando precisa consultar um banco de dados que realmente só quer uma consulta que pode acontecer em algum momento e quer gerar uma ANR, o Handler em execução é mais simples.
A   escolha de executor é também adequada quando se pretende lidar com várias solicitações de entrada em simultâneas e um Handler só pode fazer uma de cada vez.

[1] http://developer.android.com/reference/java/util/concurrent/Executors.html

[2] http://codetheory.in/android-java-executor-framework/

[3] http://stackoverflow.com/questions/7462098/handlerthread-vs-executor-when-is-one-more-appropriate-over-the-other

Deixe um comentário

O seu endereço de e-mail não será publicado.