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

Desenvolvendo para Android – Como Implementar Content Providers

Standard

Hello Team, Ja vai algum tempo desde o ultimo  pois  o tempo ficou meio curto para escrever  principalmente depois de ter começado o  nanodegree do Udacity que tem sido bastante animado, tenho aprendido varias novas técnicas no android e o melhor de tudo é poder fazer novos amigos dentro dos grupos e forums de discussões.

Para quem queira começar o desenvolvimento de apps para android ou levar os conhecimentos que ja tem ao próximo nível, pode dar uma olhada nos seguintes cursos Android Fundamentals e Android Nanodegree.

Voltando para a jornada que faziamos, no ultimo post estivemos a falar sobre como persistir os dados para a utilização offline logo após de descarregar da cloud.

Até então demos um passo realmente importante para garantir uma boa experiência de utilizadores pois em países como Moçambique( Meu amado pais) e outros países africanos onde a conectividade a Internet não é estável e disponível para todos, o utilizador poderá ver as informações mais importantes mesmo quando a ligação a internet não esta disponível. Sendo especifico, o ultimo post focou-se nos seguintes pontos:

  • Como criar um contracto que contem a informação sobre as tabelas da nossa base de dados.
  • como usar o SQLiteOpenHelper para criar a base de dados com as tabelas definidas no contracto.
  • Como usar o android testing framework para poder testar se os dados estão a ser inseridos e lidos devidamente.

Com o os pontos mencionados acima, podemos criar uma boa experiência offline, fazendo a inserção e leitura dos dados ao SQLite de forma directa utilizando os métodos expostos pela class SQLiteDatabase.

Esta forma é bem rápida e fácil de implementar mas desvantagiosa pois em casos em que queremos partilhar as informações na nossa base de dados com outras aplicações (Exemplo: a app de contactos e o calendário partilham os seus dados para outras aplicações) a outra aplicação não só teria de saber que tipo de dados precisa mas como a leitura e escrita desses dados foi feita do nosso lado.

Felizmente, no android foi criado um componente denominado Content Provider que permite que apps possam partilhar informações umas  com as outras, encapsulando todo o código assim como o tipo de armazenamento utilizado para a escrita e leitura de dados fazendo com que os desenvolvedores da outra app só precisem saber qual a Uri(ja falamos sobre isso) para a informação que desejam.

Sendo assim se quiséssemos obter na nossa aplicação um filme guardado no dispositivo através de um Content Provider, seria desencadeado o seguinte processo:

  1. Especificamos a Uri, ou por outras palavras um caminho que identifica os dados no Content Provider. Um exemplo de uma Uri e a seguinte String  : content//com.app.mymooviapp/movie/2  que representa um filme com o id 2 dentro de uma tabela movie. Esta  Uri assim como as outras contem 2 partes importantes em que a parte ‘com.app.mymooviapp’  o content authority que representa o nome de todo provider e a parte ‘/movie/2′   que representa o path,o caminho para as tabelas e neste caso em concreto para um dado existente na tabela.
  2. A Uri especificada e(add acento) mais a operação que desejamos efectuar(insert,delete,update) são  passados  ao Content Resolver   que será responsável por dividir a Uri, pegar o content Authority  para procurar pelo Provider na lista conhecida pelo sistema e após encontrar o provider, a partir do path conseguir realizar a operação especificada sobre o(s) dados no path.

A questão que viria logo após o ultimo paragrafo seria como e que o sistema tem conhecimento do nosso Content provider e a content authority.

A resposta para essa pergunta é que assim como as Activities, os Content Providers devem ser registrados no androidmanifest e é especificado o nome da classe com a sua implementação e o content authority.

3. O content resolver nos da uma resposta de acordo com a operação que queriamos efectuar.(query  retorna Cursor, inser retorna int e mutio mais como especificado na documentação.)

Sabendo agora como funcionam os Content Providers, vamos passar para  a melhor parte que e(acento) a implementação de um content providers que será responsável por ler e escrever informações sobre os filme e para tal existem os seguintes passos que temos de seguir:

  1. Criar os contratos de dados e a base de dados.(Como no post anterior)
  2. Dentro do contracto denfir o BASE_CONTENT URI que ira representar o caminho para o “topo do provider”, e dentro de cada uma das classes internas ao contrato que representam uma tabela em outras palavras  um Content URI que representa o caminho ate a tabela.
basepath

Base Content URI. URI para o topo do content provider

Content Uri e Content type strings para a tabela Movie

Exemplo de Content Uri e Content type strings para a tabela Movie dentro do Contracto

3.Pensar e entender que tipo de leituras e escritas de dados iremos fazer para que possamos construir Uris apropriadas. Esta é a parte mais difícil do processo pois temos de conseguir pensar de antemão algumas coisas que não podem estar mais claras no principio. Um exemplo do que pensar nesta fase que veremos mais abaixo é por exemplo se vamos precisar de procurar filmes pelo titulo? pelo id? etc etc.

4. Apos pensar nas possíveis operações de leitura e escrita, devemos métodos auxiliares para gerar Uris dentro das classes correspondentes as tabelas no nosso contracto. Note que cada classe ou tabela terá diferentes métodos pois para tabelas diferentes podemos ter fazer a leitura de dados de forma diferente e com parâmetros diferentes como esta neste ficheiro de contracto disponível AQUI.

5.Com as Uris criadas, devemos criar uma classe e fazer com que ela extenda de Content Provider e registrar no android manifest( caso seja criada utilizando o wizzard será registrada automaticamente). Ao estender a classe Content Provider, esta obriga-nos a implementar alguns métodos importantes como (insert, delete, update, etc).

6.IMPORTANTE: Criar variáveis do tipo de dados inteiro que irão representar cada uma das Uri’s. (Exemplo: MOVIE_ID=100).

uri matcher variables

7. Criar um Uri matcher que será responsável por mapear os inteiros especificados no passo anterior a um determinado caminho. Ao fazer o mapeamento das Uri’s, é importante ter em conta que os  parâmetros do path ao invés de ter o nome escrito, devemos colocar um # (especifica que o parâmetro e um inteiro ou numero) ou um * (especifica que o parâmetro pode ser qualquer tipo de texto) para a parte do path que pode ser variavel. Na Uri content://com.app.mymooviapp/movie/2 o especificamos o id do filme mas em situacoes reais dentro da app esse id pode ser um numero qualquer. Sendo o id um numero variavel a Uri que apropriada para colocar no matcher deve ser content://com.app.mymooviapp/movie/*  onde no fim especificamos que vem um numero que nao sabemos qual .

Uri Matcher dentro da classe Content Provider

Uri Matcher dentro da classe Content Provider

8.Depois de fazer o match de todos inteiros as Uri’s, a magia ja esta quase pronta para acontecer e um ultimo passo antes de começar fazer aquilo que e o nosso verdadeiro objectivo de escrever e ler dados, e(acento) inicializar o content provider pelo método on create que se encontra dele mesmo. Este método e o local on o provider e inicializado durante o startup da nossa aplicação e todo processamento e feito na Main thread e como vimos no post, não podemos colocar operações pesadas dentro da main thread. O ideal aqui e fazer inicializações como criar uma instância do nosso SQLiteOpenHelper, SQLiteDatabase. Uma coisa extremamente importante neste método é que retorna um boolean que significa se o provider foi bem inicializado ou não. No caso do nosso provider, não fizemos nenhuma verificação e garantimos que o provider retorna true.

on Create dentro do Content Provider

on Create dentro do Content Provider

9. O ultimo passo da implementação do content provider, é a implementação dos métodos insert, query, update, delete que são os responsáveis por fazer a inserção, leitura, actualização e remoção dos dados respectivamente. Implementar estes métodos e relativamente fácil , mas e preciso tomar em conta o seguinte:

a. Todos estes métodos recebem uma Uri quando são invocados pelo content resolver e a primeira coisa que temos de fazer e utilizar o matcher quer criamos no passo 7 para encontrar o equivalente em inteiro a string do Uri.

b. Com o valor numérico que conseguimos ao mapear a Uri, devemos realizar um Switch case para que no mesmo método possamos tomar conta de varias Uris e realizar varias operações de forma diferente.

Exemplo de implementação do método Insert

Exemplo de implementação do método Insert

c. No fim das operações, antes de retornar o tipo de dados que cada método obriga, devemos notificar ao content resolver sobre a mudança na fonte de dados para que todos os cursores associados  e esta fonte possam actualiza-los de forma instantânea.

Notifica a mudança de conteúdo

Notifica a mudança de conteúdo

Os passos acima não são um guia definitivo, mas sim os aspectos que tomei e costumo tomar em conta no momento de implementar Content Providers. Por experiência própria, a melhor forma de aprender a implementar content providers, é implementando e lendo o código de outras implementações. Um exemplo de código super interessante de implementacao de content providers, é a aplicacao do google io 2015 iosched a qual aprendi varias coisas e encontrei classes auxiliares como Lists,Maps e SelectionBuilder(A classe ideal para remover a necessidade de pensar em detalhes de queries SQL) como poderam ver na classe final do nosso Movie Provider.

Uma outra dica no momento de implementar os content providers, e efectuar os testes de cada um dos métodos de forma individual e garantir que cada um deles, cada uma das Uri’s esta a funcionar pois ao testar no fim da implementação podemos ter erros muito difíceis de resolver principalmente nas primeiras vezes. A Classe que utilizei para testar os métodos do provider encontra-se aqui.

Espero que este post tenha aumentado o vosso arsenal como de armas ninjas como desenvolvedores para android e que seja mais um passo da emocionante e fantástica aventura que temos pela frente.

Para qualquer questão, como habitual podem postar um comentário abaixo ou agora um hangout request para qualquer dia no período das 21h as 22h (Maputo GMT +2) que ficarei feliz de vos ouvir e trocar opiniões..

Link do Projecto : MyMooviApp

Abraços

DM,

Desenvolvendo para Android – Como persistir dados offline com SQLite

Standard

Hello Desenvolvedores, espero que estejam todos muito bem e bem dispostos para continuarmos com os posts da nossa serie. Só para lembrar e actualizar os que não tenham lido, no post anterior vimos:

  • como conectar a uma API utilizando as classes HttpUrlClient,
  • O que é a main thread e porque não devemos realizar operações que podem ser longas( realizar um pedido a uma api, descarregar ou descodificar uma imagem),
  • Como utilizar a classe AsyncTask para realizar operações fora da Main thread
  • e finalmente como descodificar a resposta do JSON disponibilizada por um pedido a API do openweather.org utilizando as classes JSONObject .

Só com o post anterior é possível criar aplicações para mostrar o feed de um jornal, tweets de um certo hashtag ou dados de temperatura como no nosso exemplo e da aplicação sunshine no curso do Udacity e disponibilizar no playstore. Só que apesar de funcionar, implementar uma solução desse jeito e publicar não seria uma boa experiência para o utilizador pois toda vez que ele abrisse a aplicação ou reiniciasse uma activity por algum motivo  teria de refazer os pedidos a API o que resultaria em um gasto constante de dados e de bateria por estar a utilizar o rádio do dispositivo de forma irresponsável.

Para resolver esses pequenos inconvenientes, o android oferece várias opções que permitem persistir os dados no dispositivo para que estejam disponíveis mesmo quando os dispositivo esta desligado da rede. As opções existentes são:

  • SharedPreferences : Mecanismo que permite guardar como forma de chave- valor informações dos tipos de dados primitivos. Muito prático para guardar algumas configurações como um boolean para verificar se o utilizador já fez o login antes mas pouco eficaz para guardar dados das estruturas complexas que normalmente temos nas aplicações. Este mecanismo é seguro pois só é possível ter acesso aos dados dentro da nossa aplicação.
  • Internal Storage (Memoria interna do dispositivo): este mecanismo permite guardar ficheiros directamente na memória do dispositivo. E para esclarecer qualquer questão, a escrita e leitura de ficheiros é exactamente como e feita tradicionalmente no java. Neste mecanismo só é possível ter acesso aos ficheiros pela aplicação e esta é toda apagada no momento que a aplicação é desinstalada.
  • External Storage(Memoria externa) : Este mecanismo permite guardar ficheiros directamente em uma memória auxiliar como microSD e tem a grande desvantagem de não oferecer segurança pois os ficheiros todos podem ser lidos e modificados pelo utilizador caso esteja a utilizar a memória no modo “mass storage”.
  • SQLite Database : O android oferece para base de dados em SQLite que podemos aceder pelo nome da base de dados apenas dentro da aplicação em que ela foi criada. Este mecanismo possui a vantagem de ser seguro e pratico para armazenar estruturas complexas.

A minha opinião é  de que todas as opções citadas acima são interessantes dependendo do caso em que precisaríamos utilizar mas para este post iremos utilizar a ultima opção que é  o SQLite Database.

Sendo assim, neste post iremos ver os seguintes pontos:

  • Criar uma classe que vai definir o contracto ou esquema da nossa base de dados.
  • Utilizar a classe SQLHelper para criar a base de dados
  • Utilizar o Framework de testes do android studio para realizar os testes em que iremos inserir, actualizar e remover os dados nas tabelas da nossa base de dados.

Começando pelo primeiro ponto, vamos pegar o projecto do post anterior, criar um pacote com o nome data como mostra a figura abaixo. De seguida vamos criar um classe chamada WeatherContract como esta no gist AQUI.

A classe que acabamos de criar irá conter classes internas que irão representar cada tabela da nossa base de dados, possuindo o nome das colunas e outros detalhes que iremos utilizar ao estudar content providers em posts mais a frente.

Criado o nosso contracto, vamos agora criar uma classe WeatherSQLHelper que vai extender a classe SQLiteOpenHelper. O SQLiteHelper, é uma classe que nos ajuda na criação, e gestão de versões da base de dados.

Para utilizar esta classe e criar uma base de dados, devemos criar um construtor que tenha a chamada para o método super, e reescrever o método onCreate(SQLiteDatabase db) que é o método onde todo processo de criação da base de dados e das respectivas tabelas é efectuado . Dentro desta classe definimos ainda parâmetros como o nome da  base de dados, a versão.

Ate então, o que nos resta, é  criar as queries para criação de tabelas usando o auxilio das colunas definidas estaticamente na classe do contracto acima.

Após criadas as strings para a criação das colunas, vamos , dentro do metodo onCreate, criar as tabelas utilizando o método execSQL(String string) da classe SQLiteDatabase que é  passada como construtor para o método com mostra o gist completo do helper AQUI.

Finalmente temos o nosso contracto criado, a base de dados e as tabelas.

Quando comecei a programar para android e mexi com o SQLite pela primeira vez, na hora de testar , tive de perder muito tempo para criar uma UI que me permitisse testar as operações de inserção, leitura e actualização para no fim ficar devastado ao descobrir que tinha um erro e não tinha nada haver com a UI que tinha perdido tanto tempo a criar.

Felizmente, as nossas ferramentas de desenvolveram tanto que hoje em dia o android studio possui um Framework de testes que nos permite testar só esta parte da base de dados sem que tenhamos de fazer o mesmo trabalho que tive aquela altura, permitindo testar a aplicação tomando cada parte da aplicação independente do resto da implementação.

Para começar, devemos dentro da nossa aplicação, do lado esquerdo onde tem a estrutura do projecto mudar a virtualização da estrutura do projecto para “Project” pois este pode estar em mais duas formas “Android” por default our “package”. Para tal vejam como  o fazer nas figuras abaixo.

Visualização da estrutura default- Android

Visualização da estrutura default- Android

ProjectView

A segunda figura mostra que dentro da pasta app->src, temos uma pasta chamada androidTest. Esta pasta é automaticamente criada pelo android studio quando se cria um novo projecto, e esta contem os ficheiro de testes que eventualmente podemos querer  utilizar na nossa aplicação, neste caso para testar a nossa base de dados. Vamos dentro do pacote principal dentro da pasta criar uma classe TesteDb e fazer com que a mesma extenda AndroidTestCase.

Feito isto para dar continuidade ao processo, antes de começar a escrever os métodos do teste, devemos configurar o Android Studio para que execute os testes como uma variante separada da nossa aplicação. Para isso basta:

  1. Clicar em Edit Configurations na lista que aparece antes da setinha verde do Run do IDE.

EditConfigurations

2. Adicionar uma nova configuração, clicando o botão verde com o sinal “+” mais a esquerda da janela aberta e escolher android tests.

AndroidTests

3. Copiar as configurações como estão na figura abaixo. Note  que o mais importante e  configurar o nome (Name) , escolher o “module” que normalmente costuma ser o “app” e definir que deve sempre mostrar a opção de escolher o dispositivo em que deseja executar os testes.

Configurations

Concluídas as nossas configurações, vamos voltar para o ficheiro de testes e escrever os métodos como estão no gist AQUI.

Ao clicar na setinha verde para executar a configuracao de testes, se estiverem a utilizar um dispositivo real ira mostrar a tela normal para executar no dispositivo e  os testes serao iniciados mostrando o resultado como na figura abaixo.

Tests Passed

Com os testes efectuados e todos limpinhos, chegamos ao fim de mais um post e desta vez desafio-vos a implementar  os testes que faltam como a inserção, leitura dos dados da previsão.

Mais uma vez obrigado pela atenção ao ler este post e espero poder ter contribuído no teu caminho de Ninja android dev.

Abraços!

DM

Desenvolvendo para Android – Como descarregar dados da cloud utilizando uma AsyncTask

Standard

Depois do ultimo post em que vimos mecanismos para carregar os dados  de uma fonte como um array e fazer com que sejam disponibilizados em uma ListView, vamos continuar a Série e aprender como fazer coisas mais interessantes e nos tornarmos em Desenvolvedores Android Ninjas. Várias vezes queremos fazer aplicações para mostrar horários dos filmes de um cinema, mostrar os últimos tweets de uma certa tag, mostrar os vídeos de um canal do youtube ou mostar os posts de uma determinada conta do Tumblr mas não sabemos necessariamente por onde começar e fazer com que a nossa idea ganhe vida.

Para que possamos superar estes inconvenientes, neste post irão aprender como:

  • O que é uma API?
  • Exemplos de API’s Interessantes.
  • Conectar a uma API utilizando o protocolo HTTP.
  • Realizar a operação de conexão a API dentro de uma AsyncTask
  • O que é o JSON.
  • Processar a resposta do API disponibilizado em formato de um JSON.
  • Mostrar os dados em uma ListView.

O que é uma API?

Uma  “Application Programing Interface”  ou normalmente conhecida pelo seu acrónimo API, é um conjunto de padrões ou instruções de programação para o acesso a uma aplicação   ou as suas funcionalidades sem que haja a necessidade saber como a aplicação ou funcionalidade foi desenvolvida. Várias empresas como Google, Facebook, Twitter etc, desenvolvem API’s para que os programadores possam criar e implementar aplicações terceiras que usufruam dos serviços oferecidas por eles. Uma das API’s mais comuns de ver tanto em websites como em apps, é a API dos Maps disponibilizada pela Google que permite que o Web Developer possa colocar um mapa no seu website utilizando apenas um código disponibilizado ou que mobile App Developers possam utilizar serviços de localização em suas aplicações. Abaixo estão listadas algumas API’s muito utilizadas no mundo:

Para dar continuidade ao post,  iremos de seguida escrever o código para conectar ao servidor do OpenWeather API, buscar os dados de temperatura para uma determinada cidade e de seguida processar a resposta e disponibilizar na lista do projecto criado no post anterior.

Para começar vamos abrir a classe FragmentLista e adicionar o método disponível no gist aqui. O método adicionado será responsável por fazer uma conexão pelo protocolo http a url que ele recebe como parâmetro e ira devolver uma string como resposta que nos esperamos que seja a resposta no formato JSON do pedido que tenhamos feito a url. Explicando um bocadinho o que acontece neste método,o processo e o seguinte :

  • Primeiro criamos uma url apartir da string que o método recebe como parâmetro utilizando a classe URL.
  • De seguida Criamos e inicializamos uma conexão a url criada no passo anterior. Para inicializar a conexão podemos utilizar a classe HTTPUrlConnection ou outras bibliotecas como OKHTTP(Oferecem melhor performance).
  • Por fim fazemos a leitura dos bytes do fluxo de entrada (InputStream) e vamos construindo uma String utilizando a classe BufferedReader e fazer com o método a retorne.

Ate então, se por algum motivo decidíssemos invocar o método neste momento, poderíamos nos deparar com o erro  android.os.NetworkOnMainThreadException  que é causado por estarmos a fazer uma operação de connectar a rede que não sabemos em que momento poderá terminar dentro da thread princiapl Main.

Este erro acontece porque de acordo com as especificações disponíveis aqui, a Main thread é responsável por despachar eventos para um determinado widget da interface gráfica e interagir com o Android UI toolkit (componentes do pacote android widget e  android.view).

Pelas razões citadas a pouco, realizar tarefas que não sabemos em que momento terminariam dentro da main thread vai fazer com que a nossa aplicação não consiga reagir aos eventos activados pelo utilizador e posteriormente causar com que a aplicação pare de Executar.

Dentro de uma AsyncTask somos obrigados a implementar um método de nome doInBackground mas maioria das vezes podemos querer implementar pelo menos os 3 métodos abaixo:

  • onPreExecute(): O método onPreExecute é executado antes de iniciar a nova thread no background para realizar o trabalho que nos pretendemos . Este método é executado na UI Thread.
  • doInBackground(Params… param): o método do In background é onde realizamos todas as nossas operacoes que não sabemos quanto tempo podem levar (descarregar uma imagem, connectar a uma API). Este método recebe um parâmetro  do mesmo tipo que passamos ao extender a classe e retorna também o tipo de dados definido ao extender a classe. Este método e executado fora da UI Thread.

onPostExecute(Param param): O método on post execute é invocado de volta na UI Thread e recebe como parâmetros os dados retornados pelo método doInBackground. É neste método que podemos começar a definir os dados nos componentes após o fim do método doInBackground.

No nosso caso, ao executar o pedido o servidor responde-nos com uma String em formato JSON. Mas afinal de contas o que é o JSON?

JSON significa JavaScript Object Notation, e  é um formato muito utilizado para troca de dados. O JSON tem várias vantagens como permitir  a facil leitura e escrita pelos seres humanos assim como pelos computadores.

Basicamente o JSON representa os dados em forma de chave-valor e podem ser representados tipos de dados primitivos, objectos e arrays. Para mais informações sobre o JSON a W3Schools tem um tutorial muito bom AQUI

Para descodificar a resposta do nosso servidor que está no formato JSON, vamos utilizar duas classes bastantes úteis para o efeito que são JSONObject e JSONArray. A implementação completa do AsyncTask como uma classe interna do Fragment que temos a lista encontra-se AQUI.

Com a AsyncTask criada, só nos resta modificar o código dentro do onCreate() do fragment em que fazemos o set do adapter com o array que tinhamos pre-definido, para utilizar a asynctask como esta aqui.

Conclusão

Com este post foi possível aprender o básico de como fazer conexao a uma url utilizando o protocolo HTTP e como fazer de forma correcta para  que esta tarefa seja executada em uma thread que nao afecta a responsividade da nossa aplicação. Gostaria de terminar o post dizendo que a idea do fundo deste post em particular era deixar a conhecer a classe AsyncTask como forma de realizar operacoes(descodificar bitmaps, descarregar dados da rede etc.) em uma thread separada . Para o nosso exemplo em que estávamos a fazer uma conexão a uma API, existem bibliotecas como OkHTTP e Volley que permitem fazer o mesmo de forma muito mais eficiente.

Como sempre, espero pelos feedbacks, questões etc.

Ate mais =)

DM!

Desenvolvendo para Android – Lista e Adapters

Standard

Depois dos últimos posts da serie em que estivemos a ver o básico sobre o desenvolvimento do android, chegou a hora de começar a entrar em tópicos um pouco mais complicados que necessitam a junção de todos os conhecimentos adquiridos ate a data. Neste post iremos aprender

  • Como criar uma lista.
  • Como popular os dados da lista utilizando um Adapter.
  • Como registar eventos de “click” a cada item da lista e iniciar uma Activity com os dados do item correspondente.

Resultado esperado no fim do post:

device-2015-02-25-124754 device-2015-02-25-124823

Uma ListView é um widget disponível no android utilizado para mostrar dados em forma de uma lista. Cada item da lista é uma view que pode tomar qualquer apresentação que quisermos dependendo do ficheiro de layout definido como veremos posteriormente.  Para começar, vamos criar um novo projecto, criar um Fragment, abrir o ficheiro de layout do fragment e copiar o código disponível neste gist. De seguida vamos abrir  código java do nosso  fragment e dentro do onCreateView() vamos criar uma instância da classe ListView e fazer a ligação ao widget utilizando o id definido no xml como esta disponível no gist aqui.

Nota: Como vimos no post anterior, o método onCreateView recebe 3 parametros importantes nomeadamente um LayoutInflater, ViewGroup, Bundle e como foi explicado o layout inflater e utilizado para desenhar uma View(neste caso o nosso layout do fragment disponível na pasta res->layouts). Ao criar uma instancia da classe View e utilizar o inflater fazendo View  view =inflater.inflate(int layout,ViewGroup parent,boolean attachToParent), estamos a desenhar o layout que depois e retornado pelo método getView. Para pegar encontrar um widget com um determinado id no XML, diferentemente de uma activity que faríamos TextView textView = (TextView) findViewById(R.id.text), aqui nos temos que explicitamente dizer em que view nos desejamos fazer o findViewById(int id) e para o nosso exemplo faríamos ListView list = (ListView) view.findViewById(R.id.lista). E a razão para este procedimento, e que as classes Activity, ActionBarActivity ou FragmentActivity de que extendemos possuem uma instancia da classe View em que na implementacao do método setContentView que temos acesso , o processo de desenhar o layout e feito assim como explicado acima.

Desenhada o layout do Fragment, criada a instância da lista e devidamente acoplada pelo id ao widget no ficheiro xml, vamos criar um componente bastante importante  na criação de listas. Esse componente tem o nome de Adapter e a sua funcao é de pegar os dados de uma fonte(Base de dados, array), desenhar cada item da lista e preencher os campos necessários com os dados recebidos. Para criar uma adapter vamos primeiro criar uma classe normal  atribuir um nome sugestivo e fazer com que ela estenda da classe BaseAdapter. De seguida o android studio mostra um erro e para resolver basta fazer o ctrl+i para implementar todos os métodos a ré-escrever:

  • getCount():int – O método getCount requer que se devolva um inteiro e este representa o numero de dados que queremos apresentar no listview. Se por exemplo fizermos o return de 3, o nosso listView so apresentara 3 elementos. E sempre aconselhável fazer o return do tamanho do array ou da Lista que temos.
  • getItem(int):Object –  O método getItem faz o return do item na posição passada. No nosso caso faz o return de uma string do array que encontra-se na posição passada como parâmetro.
  • getView(View,ViewGroup,int): view – O método getView é mais importante do adapter pois ele é que é responsável  por desenhar cada item da lista e reciclar um item que já não esta visível  na tela para que seja utilizado para popular os dados por vir no item da lista mais abaixo.Ele recebe uma view que no inicio da criação da lista ele sempre vem null ate a criação de todas as views visíveis na tela. No momento em que o utilizador faz o scroll, ele pega as views que não estão mais visíveis para o utilizador e faz a reciclagem para que sejam re-utilizadas para popular as views que estão por vir com novos dados da lista. Neste método devemos seguir os seguintes passos  para desenhar a view e popular com os dados :
  • Pegar o Context da aplicação utilizando o ViewGroup passado ou ate uma variável global passada ao construtor do Adapter.
  • Criar um LayoutInflater a partir do Context que utilizaremos para desenhar a view.
  • Utilizar o inflater para desenhar a view a partir do ficheiro de layout que representa cada item da lista.
  • Fazer os findViewById(int) dos componentes que temos dentro do layout da linha da lista.
  • Popular estes componentes com dados.

Fazer o return da view criada dentro do método.

A implementação comum do getView encontra-se aqui. O mau desta  implementação e que sempre que entramos  neste método e a view for null, teremos de fazer sempre uma nova instância e chamar o findViewById o que não é muito bom em termos de eficiência. Para resolver este ficheiro basta criar um ViewHolder que não é nada mais que um classe que contem a instância de todos os nossos componentes, onde fazemos o findViewById uma única vez e no nosso getView simplesmente pegamos uma instância deste objecto e os componentes la dentro já estão  devidamente mapeados aos widgets no xml e temos apenas de popular com os dados. A implementação final do adapter em que  utilizamos o ViewHolder encontra-se aqui.

Com o Adapter pronto, o que temos de fazer de seguida é voltar para o nosso fragment, criar uma instância do nosso adapter, passar os dados que queremos apresentar na lista(neste caso um array de Strings) e de seguida fazer definir o adapter fazendo listView.setAdapter(Adapter) como esta no seguinte gist.

Com todo o código que temos ate agora, ao executar a aplicação conseguiremos ver uma lista com os dados que definimos.

Para terminar o post, vamos fazer com que ao clicar em algum item da lista, seja chamada outra Activity e que esta nos deve mostrar os dados do item da lista que clicamos. Para isso primeiro temos de criar uma Activity( botão direito do Mouse -> new -> Activity -> Blank Activity) e de seguida modificar o id do textView do ficheiro de layout da Activity para que tenha um identificador para que possamos definir o texto que vem da nossa lista(Gist). De seguida temos de voltar ao fragment que tem a nossa lista e definir o onItemClickListener listener que ficara a escuta de eventos de click a cada item da nossa lista. Na implementação deste método iremos pegar o valor do item naquela posição da lista utilizando o adapter passado ao método, criar um Intent para iniciar uma nova activity como vimos aqui() e passamos uma string como extra atribuindo uma chave única terminando por iniciar a nova activity fazendo startActivity(Intent) como esta no gist final do Fragment.  Para completar voltamos a Segunda Activity e após ter uma instância do TextView e mapear ao widget no xml pelo seu id, devemos pegar o Intent que invocou a nossa activity, pegar a String passada como argumento utilizando uma chave e fazer o setText no textView(Gist). Para terminar, só nos falta executar a aplicação e testar o que estivemos a fazer ao longo do post.

Desafio: Modifiquem a vossa lista para que passe a mostrar um icon e o nome do lado.

Dica: irão precisar de um array de icons do mesmo tamanho que o de nomes e modificar o layout responsável pelo desenho de cada item da lista assim como o método getView.

Links:

Projecto no github

ListaeAdapter.apk – ficheiro de instalacao da aplicacao

Como sempre, muito obrigado pelo tempo de leitura e ficarei a espera de comentários, duvidas ou sugestões de melhoria.

Abraços

DM

Desenvolvendo para android- Fragments

Standard

Depois de entender sobre a base de uma aplicação em android no ultimo post sobre ciclo de vida de uma Activity disponível  AQUI, chegou a hora de entender  o funcionamento de um componente tão importante como uma Activity. Um dos maiores desafios que os desenvolvedores encontram  no desenvolvimento de apps para android,  e a existência de vários dispositivos com  diversas formas e tamanhos.  Para criar uma aplicação que possa adaptar-se aos diversos dispositivos no mercado, é preciso entender componentes como Fragments e como estes podem ajudar-nos nesta tarefa.  Este post iremos aprender sobre :

  • O que são Fragments.
  • Como Funcionam os Fragments
  • Como utilizar os Fragments nas aplicações.

Para começar o post definindo e entendendo o que é um Fragment em apenas uma frase, eu diria que “Fragments são sub-Activities” e a  razão pela qual o descrevi em uma frase tão curta é que os fragments tem um ciclo de vida como uma Activity, tem interface gráfica(podem não ter) e estes só podem existir dentro de uma Activity. Indo um pouco a historia do android, os fragments apareceram pela primeira vez na API 11(Versao 3.0) com o surgimento dos primeiros tablets.

Estes novos dispositivos para o android originaram a necessidade de utilizar de forma eficiente o espaço que  apresentavam e proporcionar uma experiencia apropriada ao utilizador. A solução para este problema foi utilizar os Fragments como partes modulares da interface gráfica que podem ser combinadas ou separadas consoante o espaço do dispositivo. Para entender melhor esta ultima parte vamos tomar em considerarão o Gmail admitindo que esta é uma aplicação muito familiar para todos e considerar os cenários onde o utilizador abre a app utilizando um smartphone  ou um tablet.

Comecemos com o caso em que o utilizador abre a aplicação utilizando um smartphone e a primeira coisa que ele vê e uma Activity com a lista de e-mails recebidos na caixa de entrada e ao clicar em um dos e-mails, outra activity é aberta e esta  apresenta o conteúdo do e-mail como mostram as figuras abaixo.

Tela com a lista dos emails no Gmail

Tela com a lista dos emails no Gmail

Screenshot_2015-02-13-23-05-56

No segundo caso, o utilizador esta a utilizar um tablet e quando ele abre a aplicação, esta apresenta uma activity divida em 2 áreas onde mais a esquerda esta a lista dos e-mails da caixa de entrada e ao clicar em um dos e-mails, o conteúdo e apresentado na área maior a direita como mostra a figura abaixo.

unnamed

fragments

Passando para a parte interessante que e a criação de interfaces dinâmicas utilizando Fragments, vamos criar o nosso primeiro Fragment e para tal basta criar uma classe e estende-la da classe Fragment.(E sempre aconselhável utilizar a classe da biblioteca de suporte.)

Como disse antes, assim como o as Activities os Fragments tem um ciclo de vida e métodos de callback que representam cada estagio como e explicado aqui(Fragment LifeCycle Link). Para  este primeiro exemplo, os 2 métodos de callback importantes a saber sao são:

  • onCreate: Assim como uma Activity, o Fragment possui este método que e invocado quando ele primeiramente criado e todas configuracoes globais necessárias podem ser e e aconselhavel que seja feitas aqui.
  • onCreateView: Diferente da activity em que a interface gráfica é desenhada pelo setContentView(), o Fragment possui este método de callback que é responsável por desenha r e fazer o retorno da interface que pretendemos associar ao Fragment. Este método recebe 3 parâmetros: um LayoutInflater responsável por desenhar uma view, um ViewGroup como pai da view que vamos desenhar e um Bundle com o estado anterior.

Só com os dois métodos acima, já estamos prontos para utilizar os Fragments e vamos criar um gist como no seguinte gist do FragmentA e re-criar um segundo Fragment B.

Após criar os Fragments devemos colocar dentro de uma Activity e para tal existem duas formas de fazer isso:

  • Estaticamente: Colocar o fragment estaticamente dentro de uma Activity é feito declarando o Fragment directamente no ficheiro xml do layout assim como declaramos um TextView, Button ou EditText. O colocar o Fragment desta maneira ele ficara ligado aquela Activity para sempre e não poderá ser modificado durante a execução da aplicação. Um exemplo muito pratico da utilidade deste método e para criar interfaces com dois painéis em um única Activity como vimos acima. O exemplo desta técnica encontra-se AQUI
  • Dinamicamente: Colocar os fragments dinamicamente uma das formas mais utilizadas pelo simples facto de poder-se adicionar, alterar ou remover um Fragment de uma Activity enquanto executamos a aplicação. Esta técnica é muito utilizada quando se usa Navigation Drawer(menu de navegação lateral) e ao seleccionar em cada opção aparentemente parece ser uma outra Activity já que a interface gráfica vai mudando mas é na verdade uma troca de Fragments. Aqui  não é o Fragment que é directamente colocado no ficheiro de xml mas sim um FrameLayout que servira de contentor para colocar os Fragments dinamicamente com no gist.

Os ficheiros xml mencionados nas duas técnicas acima tem de ter o mesmo nome e são colocados em pastas devidamente especificadas para que ao executar a aplicação, dependendo do dispositivo e das configurações o android saiba para que pasta recorrer e buscar os ficheiros que precisa. Para o caso em que estamos a trabalhar, devemos colocar o ficheiro xml com  o Framelayout dentro da pasta res->layout e o outro com os dois Fragments definidos estaticamente na pasta layout-sw600 que significa que sera apenas carregado quando o tamano mínimo da tela seja de 600 dp. Para mais informações sobre como organizar os recursos em pastas veja AQUI.

O código da MainActivity esta escrito de tal forma que ao executar a aplicação, durante o onCreate da Activity e primeiramente feito o setContentView e apontamos para o ficheiro xml mencionado acima. De seguida mapeamos o FrameLayout utilizando o id definido em um dos ficheiros xml. Se o utilizador estiver a utilizar um tablet, porque não existe especificado um FrameLayout no ficheiro de layout, teremos a variável com um valor nulo e podemos criar um novo caminho para o código que sera executado no modo do tablet e os Fragments definidos estaticamente estarão la. Caso a variável  do FrameLayout não seja nula significa que estamos a utilizar um smartphone e teremos que colocar o fragment dentro do nosso frameLayout como no seguinte gist.

Espero que depois deste post, tenhamos aprendido um pouco sobre Fragments  e como podemos os utilizar para criar interfaces graficas mais ricas e dinamicas….

Como sempre fico a espera pelos comentários, criticas e um feedback acerca do que pode ser melhorado!

Obrigado Ate a proxima

Desenvolvendo para Android – Ciclo de Vida de uma Activity

Standard

Ola a todos leitores e bem vindos a mais um post da série “Desenvolvendo para Android”.  Para quem não pode acompanhar os posts iniciais, pode encontrar o primeiro post AQUI. No ultimo post, falamos sobre Intents o mecanismo que permite a comunicação entre componentes internos de uma app ou com os componentes de de uma outra app. Resumindo, o post foi em volta de como utilizar intents para iniciar uma activity, passar uma informação de uma activity para outra e como  fazer o uso de intents para inicializar activities externas a nossa aplicação na realização de acções que possamos precisar(enviar um e-mail, tirar uma foto, realizar uma chamada, etc).

Como tivemos a oportunidade de ver Post passado, a inicialização de uma Activity é bem diferente do conceito que todos aprendemos durante as aulas de programação orientada a objectos. Durante o aprendizado de java, se quiséssemos inciar uma activity, primeiro teriamos de criar uma instância do objecto da mesma e o código seria executado a partir do método main. No android, a Activity não tem metodo main mas sim “callback methods” que são métodos que são invocados no inicio de uma activity ,mudança na orientação do dispositivo, recepção de uma chamada, mudança para uma outra aplicação dentre outros cenários que podem surgir. Esses “callback methods” são chamados e um certa sequência criando um ciclo que e denominado o ciclo de vida de uma Activity. e é importante saber como e quando e invocado cada um deles de forma que possamos sempre prever algumas das situações citadas acima e tomar as medidas necessárias para garantir a continuidade uma boa experiência ao utilizador.

basic-lifecycle

A figura acima mostra o ciclo de vida de uma Activty e a ordem como os “callback methods” são chamados.  Tudo comeca quando clicamos no icon de uma app no “launcher”  ou “home  screen” do dispositivo e os métodos são chamados na  seguinte sequência:

  • onCreate(Bundle SavedInstanceState): O método onCreate é o primeiro método a ser invocado ao iniciar um Activity, dentro deste método e onde são feitas as inicializações ou configurações globais da Activity(caso existam) como definir o layout da Activity pelo método setContentView ,Criar instâncias de “Widgets” e mapear aos equivalentes no XML, etc. Este método recebe como parâmetro um Bundle que iremos ver em detalhe mais a frente no post.
  • onStart: O método onStart é invocado assim que a activity é criada ou quando é re-iniciada após ter sido parada nalgum momento. Durante o método onStart, a Activity já esta visível para o utilizador. Este método é normalmente utilizado com contra método para o onStop visto que após para a Activity e possivel que desejemos devolver o estado que a Activity se encontrava antes de ser parada.
  • onResume(): Assim como o metodo onStart, o onResume e invocado quando a activity ja esta visivel. Este metodo e normalmente como contra metodo do onPause para inicializar os componentes ou recursos libertados durante o onPause.
  • onPause(): O metodo onPause e chamado quando a Activiy fica parcialmente por de tras de uma outra Activity que utilizador esteja a utilizar. Este metodo e normalmente utilizado para libertar recursos que nao estao a ser utilizados de momento que podem comprometer a performance do dispositivo. é sempre bom começar a libertar os recursos durante o onPause pois a invocação deste método, e um indicador de que o utilizador pode nao voltar mais para a aplicação.
  • onStop(): O método onStop é invocado no momento em que o utilizador sai de uma Activity ou muda de aplicação e esta deixa de estar visível para ele. A invocação deste método é também um indicador de que o utilizador não voltara mais e é preciso libertar todos recursos que não serão mais necessários. Note que na figura acima existe uma certa sequência de invocação dos método pelo que para que seja invocado o onStop, terá de ser invocado primeiro o onPause.
  • onRestart(): O método onRestar é invocado quando voltamos a abrir a Activity de uma app após ter sido “escondida” antes. Um exemplo comum de invocação do onRestart, e quando voltamos a uma activity utilizando a janela de apps recentes(“Recent apps” ) que mostra quais as activities mais recentes tínhamos abertas. Segundo a documentação,  maior parte das aplicacoes nao precisam re-escrever o metodo onRestart e logo apos a sua invocacao, o metodo onStart é invocado.
  • onDestroy(): Finalmente, o ultimo método do ciclo de vida de uma Activity e o onDestroy(). O onDestroy é invocado naquele último suspiro da activity e nesse momento a Activity é destruída na memoria. O onDestroy() é normalmente invocado nos casos em que uma activity é parada por muito tempo e por uma questão de uma boa gestão de recursos, o sistema elimina a activity da memoria para a utilização por outros processos. O onDestroy() também e invocado caso exista uma instrução que explicitamente invoque o método finish() para terminar com a Activity.

Para este post, vamos ré-criar uma nova aplicação com apenas uma activity e utilizando a API de Notificações do android, vamos emitir uma notificação para cada método do ciclo de vida de uma Activity e verificar com precisão que métodos são chamados e em que situação são chamados. Para começar, vamos criar um novo Projecto como vimos no outro post AQUI. Após  criar o projecto, para fazer o primeiro teste, basta copiar o Gist e correr a aplicação. Ao correr a aplicação, o sistema ira apresentar notificações assim que a activity for destruída, recriada, parada parcialmente etc como mostra na figura abaixo.

Noti

Noti

Um dos motivos tão importante de entender o ciclo de vida de uma activity, é a necessidade de manter uma experiência boa para o utilizador de forma continua independentemente da mudança de configurações ou outro incidente que faça com que a activity seja recriada. Para este post vamos tomar em consideração  o simples e frequente caso em que o utilizador muda a orientação do dispositivo e nesse momento já tinha dados carregados, a inserir, ou alguma outra operação que não pudesse ser interrompida. Para  resolver este problema existem duas opções:

  • a primeira é definir a propriedade da activity para que esta não mude de orientação mesmo que o utilizador mude a orientação do dispositivo.
  • A segunda opção e fazer o uso dos métodos de callback da activity de forma a repor o seu estado mesmo depois de recriada.

A primeira opcao é um truque interessante, mas para este post a segunda e a que mais interessa. Para podermos guardar o estado de uma activity e posteriormente repor caso esta seja ré-criada, devemos implementar o método onSaveInstanceState que recebe como  parâmetro um objecto do tipo Bundle que ira guardar as informacoes utilizando uma combinação chave, valor para qualquer tipo de dados primitivo e classes que implementam Serializable and Parcelable(Prometo escrever um post só sobre isto). Este metodo e automaticamente invocado pela a activity logo depois do metodo onStop. Para ilustrar como poderíamos fazer o uso desta técnica veja o seguinte Gist.

Desta forma chegamos ao fim de mais um post e para mais informações ai vão alguns links interessantes:

Muito obrigado amigos e ate a proxima  J