Tutorial Android Executor – Parte 1
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.
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.
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á
- 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