sexta-feira, 20 de setembro de 2013

Desvendando o AsyncTask

Introdução

Quando vamos desenvolver uma aplicação mobile, em algum momento nós sempre precisamos implementar algum tipo de requisição de alguma informação, seja ela online ou na própria base de dados local do Android.

Sabemos que, muitas vezes, esse tipo de requisição pode levar algum tempo pois não depende somente da aplicação, do seu código. Sempre irá existir algum delay realizando uma requisição.

O grande problema desse tipo de requisição é que a nossa aplicação, no momento que é iniciada, recebe a Thread principal do Android (Thread reponsável pelo UI) para trabalhar e quando usamos, indevidamente, essa Thread para realizar uma requisição, pode, muitas vezes, travar a aplicação, ou até mesmo a requisição ser cancelada pelo próprio Android para evitar problemas maiores com o funcionamento do S.O.

Para resolver esse problema, existe uma Classe abstrata chamada AsyncTask onde tem a finalidade de realizar a requisição em uma Thread separada no background do Android deixando a Thread principal livre para continuar trabalhando. Dessa forma, é possível realizar requisições sem comprometer o desempenho do S.O.

O que é o AsyncTask?
Como dito acima, o AsyncTask é uma Classe Abstrata utilizada para execução de metodos em uma thread background, deixando livre a Thread UI (Thread Principal).

A utilização da AsyncTask é para  processos de poucos segundos, caso seja necessário realizar processamentos mais demorados, é aconcelhável utilizar as opções de APIs presentes no package "java.util.concurrent", como indicado na documentação.

Quais são os métodos principais?

Essas métodos descritos abaixo, em nenhum momento, podem ser chamados diretamente. Eles são executados em uma ordem específica e cada um tem sua finalidade, vamos conhece-los?
  • onPreExecute()
    • Este é o primeiro método chamado quando executado o execute(Params...);
    • Este método é utilizado, entre outras coisas, para realizar as configurações do processo e/ou ativar algum Status Loader indicando que está acontecendo algum processo.
  • doInBackground(Params...)
    • Este método já é chamado após o método onPreExecute() ser finalizado. É nele que o processo será realizado em uma thread separada da Thread Principal;
    • Aqui você também poderá chamar o método publishProgress(Progress...) informando o valor de andamento do seu processo, o mesmo, quando chamado, irá executar o onProgressUpdate(Progress...) passando esse valor que você informou no publishProgress(Progress...);
    • Também é aconcelhável que, no método, seja validado sempre o método isCancelled() (este método retornará TRUE caso sejá chamado o método cancel()) para, caso retornar TRUE, você realize a parada do seu processamento.
  • onProgressUpdate(Progress...)
    • Este método é chamado quando o método publishProgress(Progress...) for acionado;
    • Ele é usado para dar um feedback do andamento do processo. Podemos, aqui, atualizar o status de uma barra de progresso, por exemplo;
    • Ele não tem um timeout, pois o método doInBackground(Params...) continua com seu processamento em outra thread, não sendo interferido pelo processamento do onProgressUpdate(Progress...).
  • onPostExecute(Result)
    • Por fim, este método é chamado logo após o término do método onInBackground(Params...);
    • Ele recebe o retorno do onInBackground(Params...). A partir do tipo configurado como retorno na configuração da Classe. É aqui que você terá a oportunidade de trabalhar com o Parâmetro recebido, por exemplo, caso você tenha feito o download de uma image, aqui você irá receber os bytes desta imagem e irá coloca-la no seu ImageView.

Como utilizar o AsyncTask?

Para utilizar o AsyncTask, é muito simples. Você só precisa criar uma classe e extender do AsyncTask como abaixo:
    
private class ConsultaCEPCorreios extends AsyncTask<Params, Progress, Result>
  • Params: Tipo do Objeto que será passado por parâmetro, caso não precise mandar algum parâmetro, colocar colo "Void";
  • Progress: Tipo do Objeto usado para informar o progresso do processo, utilizados no método publishProgress(Progress...) e onProgressUpdate(Progress...);
  • Result: Tipo do Objeto de Retorno. Usado no método onInBackground(Params...) como sendo o seu retorno e o onPostExecute(Result).
Depois de configurado os métodos específicos para poder trabalhar com o AsyncTask, você só precisa chamar o método execute(Params...) para que o método seja executado.

Quando foi criado o AsyncTask, o mesmo era executado sempre em uma fila no background, sempre esperando um AsyncTask ser finalizado para depois outro ser executado. A partir da versão 1.6 do android, ele se tornou assíncrono, não existindo mais essa fila no background. Porém, a partir da versão 3.0 a fila voltou a ser o padrão para evitar erros de concorrência.
Caso seja necessário executar uma lista de AsyncTask assíncronas, será necessário trabalhar com o método executeOnExecutor(Executor, Params...) presente na classe AsyncTask.

É possível, a qualquer momento, chamar o método getStatus() onde irá retornar o status atual da execução do processo(AsyncTask.Status).

A qualquer momento é possível chamar o método cancel(boolean mayInterruptIfRunning) para executar o cancelamento do processo. Passando TRUE por parâmetro, o cancelamento será imediato sendo cancelado a Thread que está executando o mesmo, caso for FALSE, é permitido o término do processo em questão para depois finaliza-la. O concelho é que no seu processo seja sempre verificado o método isCancelled() para que o seu processo seja finalizado da melhor forma.

Depois de chamar o método execute(Params...) você também pode chamar o método get()/get(long timeout, TimeUnit unit) para pegar o Result do processo realizado. Este método é útil caso você não vá utilizar o onPostExecute(Result) para dar um destino para o Result. Quando chamado, o método fica aguardando o final do processamento da AsyncTask para depois retornar o valor. É possível configurar um valor de tempo para o timeout passando o mesmo por parâmetro, para, caso o processamento demore muito, seja cancelado o get.


Vamos ao que interessa?

Segue o link do github de um simples exemplo que fiz no Android Studio utilizando o AsyncTask e abaixo estou colocando somente o código da classe que foi implementado para realizar a busca de um endereço a partir de um cep.
    
private class ConsultaCEPCorreios extends AsyncTask<String, Integer, String> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            baseProgressBar.setVisibility(View.VISIBLE);
        }

        @Override
        protected String doInBackground(String... params) {
            String result = "";
            String url = "http://cep.republicavirtual.com.br/web_cep.php?formato=json&cep="+params[0];
            HttpClient httpClient = new DefaultHttpClient();
            try {
                HttpResponse response = httpClient.execute(new HttpGet(url));
                result += getStringFromInputStream(response.getEntity().getContent());
            } catch (IOException e) {
                Log.e("TAG_ASYNC_TASK", e.getMessage());
                baseProgressBar.setVisibility(View.INVISIBLE);
            }
            return result;
        }

        @Override
        protected void onPostExecute(String s) {
            baseProgressBar.setVisibility(View.INVISIBLE);
            tvEndereco.setText(s);
        }

        private String getStringFromInputStream(InputStream is) {
            BufferedReader br = null;
            StringBuilder sb = new StringBuilder();
            String line;
            try {
                br = new BufferedReader(new InputStreamReader(is));
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (br != null) {
                    try {
                        br.close();
                    } catch (IOException e) {
                        Log.e("ASYNC_TASK", e.getMessage());
                    }
                }
            }
            return sb.toString();
        }
    }
Adianto que o código não está seguindo as boas práticas do desenvolvimento Android, e tem como única finalidade mostrar a utilidade do AsyncTask.

Só de olhar o código, já podemos ver vários trechos que vimos no texto acima. Acredito que se você der uma boa olhada no código do exemplo em complemento a documentação, irá tirar de uma vez por todas suas dúvidas de como utilizar o AsyncTask.

Qual material relevante para estudo sobre o AsyncTask?
Bem pessoal, o post ficou grande mas acredito que conseguimos espalhar bem os pontos do AsyncTask.
Acredito que agora, no mínimo, suas dúvidas sobre ele tenham dimínuido.

Caso surja alguma dúvida ou mesmo algum ponto que deixei de lado ou mesmo eu tenha me equivocado, por favor, o campo de comentários está logo aqui em baixo! ;)

Tenham bons estudos, grande abraço

[]~

3 comentários:

  1. Gostaria de enviar dados de um cadastro de usuario para um web service via android utilizando o AsyncTask, como seria o procedimento.
    Obs. Meu web service já está funcionando e também já consigo enviar dados através do java, a dúvida é apenas no android.

    ResponderExcluir