Desenvolvendo para Android – Utilizando Loaders

Standard

Hello para todos os devs, espero que todos estejam muito bem e aprendendo com esta série sobre desenvolvimento para android/ Para quem esteja começando a embarcar no mundo do bonequinho verde sugiro que veja o inicio da serie neste POST

Dando continuidade ao que temos vindo a aprender, hoje irei escrever sobre uma API, que vem disponível por defeito na plataforma mas infelizmente é muito pouco utilizada pelos desenvolvedores.

Falo da Loaders API, que é uma API que oferece mecanismos para efectuar o carregamento de dados de forma assíncrona (Para quem quiser, tem uma boa descrição sobre Main / UI thread e background operations AQUI  ).

Como disse acima, a Loaders API foi inicialmente introduzida no framework na versão do Android 3.0 e posteriormente introduzida na android support library v4 para que pudesse ser utilizada em versões inferiores. Esta API vem com varias vantagens comparadas ao AsyncTask e possui as seguintes características:

  • Disponiveis para todos Fragments e Activities: Diferentemente dos AsyncTasks, os Loaders estão ligados ao ciclo de vida das Activities ou Fragments, o que significa que se a Activity é obstruída por outra e vai para o estado onStop, o loader também será parado.
  • Monitoram a fonte de dados: Os loaders, permitem monitorar a fonte de dados a que estão ligados, permitindo que no momento que este verifica uma mudança nos dados, ele ira automaticamente enviar os novos dados para serem actualizados na UI.
  • Carregam a ultimo Loader criado:  Voltando para o primeiro ponto das características dos loaders, porque estas estão ligadas ao ciclo de vida das Activities ou Fragments, se uma Acitivity mudar de configuracao (Orientação de Portrait  para Landscape), no momento que a Activity for recriada, o loader ira pegar a ultima instância que contem os dados carregados antes da Activity ser destruída e voltar a apresentar a UI.

Ao longo deste post, irei demonstrar como e onde implementei os loaders durante o projecto MyMovieApp do nanodegree do udacity.

Durante o desenvolvimento do MyMovieApp do udacity, deparei-me com dois momentos que precisava de carregar dados e apresentar na UI.

  1. Precisava de carregar os filmes mais populares e mais votados a partir da API do themoviedb.com.
  2. Uma vez marcados como favoritos, os filmes eram guardados na base de dados e ao seleccionar  filmes favoritos, estes tinham de ser carregados e apresentados na UI.

A Loaders API oferece a class Loader que pode ser extendida de acordo com a funcionalidade(tipo de dados que pretendemos carregar) que se quer dar ao loader. Tomando como exemplo os 2 pontos citados acima, durante a implementação no projecto utilizei duas classes classes que estendem a classe  Loader (AsyncTaskLoader e CursorLoader)

Começando pela classe AsyncTaskLoader, a definição é que esta classe é um loader que oferece uma AsyncTask para efectuar operacoes em uma background thread. Como nas AsyncTasks, esta classe requer que o metodo doInBackground() seja implementado pois é dentro deste método que devemos realizar as operações que podem levar muito tempo.(descodificar bitmaps, Carregar informação da cloud etc).

A implementacao deste loader é muito simples e para tal basta seguir os passos abaixo:

  1. Criar uma classe e extender a class AsyncTaskLoader.
  2. Especificar o tipo de dados que o loader deve retornar assim que acabar de fazer os seu trabalho.
  3. implementar a logica para realizar o trabalho dentro do método doinBackground.
  4. Retornar o tipo de dados especificado na definicao do loader dentro do método doInBackground.
//Extender a class AsyncTaskLoader e especificar o tipo de dados a ser retornado
public class MovieAsyncLoader extends AsyncTaskLoader<List<Movie>> {

    private Context mContext;
    
    public MovieAsyncLoader(Context context, String queryType) {
        super(context);
        mContext = context;
    }

    @Override
    public List<Movie> loadInBackground() {

        //Implementar toda logica e retornar uma lista de filmes
        List<Movie> movies = new ArrayList<Movie>();

        return movies;
    }

}

Seguidos os passos, temos um AsyncTaskLoader rapidamente criado e com a logica para carregar os dados implementada dentro do metodo doInBackground como mostra o codigo acima. O código completo utilizado no MyMooviApp Encontra-se AQUI.

Os próximos passos a seguir são: inicializar o loader, deixar que este faca o trabalho, mostrar os dados na UI no fim do trabalho.

Como mencionado acima os loaders so podem existir dentro de uma Activity ou Fragment. Este requesito leva-nos a mais uma classe dentro desta API denominada LoaderManager.

O LoaderManager é uma classe abstracta e esta directamente associada as Activities our Fragments para gerir uma ou mais instâncias de Loaders dentro da aplicação. Uma aspecto a tomar em consideracao e que uma Activity ou Fragment só pode ter um LoaderManager. Esta classe Loader Manager, possui uma interface chamada LoaderCallbacks que como o nome sugeri, define métodos de callback que serão utilizados para  interagir com o Loader.

Os três métodos de callback que tem de ser rescritos são:

  • onCreateLoader –  Este método e invocado assim que e especificado o metodo initLoader para  inicializar um loader. A sua implementação é a criação de um Loader que será retornado ao loader manager para ser inicializado logo de seguida pelo LoaderManager.
  • onLoadFinished: Este método é invocado assim que o  loader acaba de fazer o trabalho e os resultados são enviados de volta para UI thread. Neste método podemos interagir com a UI e passar os dados para um adapter  ou qualquer outro componente.
  • onLoadReset : Este método serve para reiniciar o loader e dar o comando para reiniciar o trabalho. fazer o initLoader tambem tem o mesmo efeito entretanto ao faze-lo dessa forma, o loader manager vai pegar a instância antiga do loader e re-apresentar os dados que ja tinham sido carregados anteriormente.

Com os métodos todos implementados, o ultimo passo para executar o loader, temos de  pegar o loader manager e invocar o método initLoader. Este método recebe  3 parâmetros:

  • Loader ID :Um inteiro que será utilizado pelo loader manager para identificar o loader que este tem de iniciar ou fazer restart.
  • Bundle : Este bundle é utilizado para passar um conjunto de informações para o loader. Por exemplo no  MyMovieApp passei um bundle onde coloquei uma string com com o tipo de query a realizar (Popular, top Rated , Favorite);
  • Callback methods : Como ultimo parâmetro temos de passar uma referencia aos métodos de callback que temos implementados. Como normalmente implementamos na definicao da Activity ou Fragment, podemos simplesmente passar este parametro como this.
getLoaderManager().restartLoader(LOADER_ID, bundle, this)
public class BlogLoadersActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<Movie>> {

    private GridView moviesGrid;

    private final int LOADER_ID=1;

    BlogCursorAdapter mMoviesAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_blog_loaders);

        moviesGrid = (GridView)findViewById(R.id.movies_grid);

        moviesGrid.setAdapter(mMoviesAdapter);

        Bundle b = new Bundle();
        b.putString("QueryType","favorite");
        //inicializa o loader passando o id, o bundle com informacao
        getSupportLoaderManager().initLoader(LOADER_ID, b,this);
    }

    @Override
    public Loader<List<Movie>> onCreateLoader(int id, Bundle args) {

        //Extraimos os dados do Bundle
        String queryType = args.getString("QueryType");

        //Inicializa e o loader e retorna ao loader Manager
        return new MovieAsyncLoader(this,queryType);
    }

    @Override
    public void onLoadFinished(Loader<List<Movie>> loader, List<Movie> data) {

        //verifica se a lista e recebida e null
        if(data!=null){
            //popular os dados na Aqui
            //definir os dados no adapter etc.
            //mAdapter.swapData(data);

        }
    }

    @Override
    public void onLoaderReset(Loader<List<Movie>> loader) {

    }
}

Com o código acima, consegui resolver o primeiro problema de conexão a API utilizando AsyncTasksLoader. A implementacao ompleta do fragment utilizado no projecto encontra-se AQUI.

Passando para o segundo momento em que precisava de carregar dados através de um Content Provider, após aprender sobre AsyncTaskLoaders poderia implementar essa funcionalidade da mesma forma apenas mudando o tipo de dados para Cursor mas o framework ja dispõe de um Loader que retorna um Cursor denominado CursorLoader.

A utilização do CursorLoader  bastante simples e para poder utilizar é preciso garantir os seguintes requisitos:

  • Ter o Content provider devidamente criado – Para quem nao sabe como o fazer pode ver o post anterior AQUI.
  • Ao implementar os LoaderCallbacks na Activity ou fragment, definir o Cursor como tipo de dados a retornar :
    public class BlogLoadersActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>
  • Implementar os métodos de callbacks explicados mais acima.
  • Ao implementar o metodo onCreateLoader, devemos inicializar e retornar um CursorLoader e por fim executar. PS: Os parâmetros passados ao CursorLoader, são similares aos que são passado ao invocar o método query do contentresolver como vimos no POST Anterior quando falávamos de providers.

    Com a simples implementação do AsyncTaskLoader e  CursorLoader, chegamos ao fim de mais um post e deste modo ja podemos ate agora construir uma app que descarrega dados da cloud, armazena a informação no dispositivo, oferece um mecanismo de leitura de dados independente da forma como estes foram armazenados e mais do que tudo consegue realizar todas estas operações de forma eficiente e seguindo as boas praticas.

Espero que tenham gostado do post e ate a próxima !

Links : Documentação oficial sobre Loaders

DM 🙂

Advertisements

One thought on “Desenvolvendo para Android – Utilizando Loaders

  1. Job

    Viva Dario, seu post sobre Loaders foi excelente!!! Gostei da simplicidade e objectividade do post. Afinal Loaders não são nada complicados. Seus simples exemplos ajudaram-me a fechar meus “issues” 🙂

    Em suma: O melhor em post em português sobre o tema 😉

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s