Atualmente as aplicações Android estão cada vez mais comuns e com isso surge a necessidade delas fazerem algumas tarefas genéricas, como acessar a um Web Service qualquer. Por exemplo, é possível acessar um servidor de aplicação e recuperar informações do banco de dados.


Guia do artigo:

Algo muito importante que deve ser lembrado é que NUNCA um aplicativo Android deve acessar diretamente um banco de dados para recuperar ou adicionar informações. Isso porque, primeiramente, não seria nada seguro: imagine um sistema em que uma aplicação Android se conectasse a um banco de dados remoto e trabalhasse com os dados sem nenhum tipo de intermediário. Outro ponto que devemos lembrar é que o acesso a um web service é muito importante por questões de padronização, pois um mesmo cliente SPA (Single Page Application) pode fazer um acesso ao mesmo servidor com uma aplicação Android ou iOS apenas seguindo um padrão de requisições e interpretando corretamente as respostas.

Na próxima seção vamos entender melhor como funciona esse padrão de requisições.

Trabalhando com requisições

Para criar um web service existem muitas maneiras e a mais simples delas é usando as requisições GET e POST, que podem ser feitas como em um formulário HTML. Existe também o formato REST, que é muito utilizado atualmente e seu retorno normalmente é um JSON contendo as informações. Esse executa em cima do protocolo de aplicação HTTP. Existe também o protocolo SOAP, mas esse não é muito utilizado por ter uma resposta um pouco exagerada para sistemas mobile.

O protocolo HTTP é definido pelas RFCs 1945 e 2616, que definem a estrutura de mensagens que serão trocadas entre o cliente o servidor de aplicação. O formato geral de uma requisição é bem simples, pois possui uma linha onde se localiza o método que requisitará, a URL e a versão do protocolo.

Na Listagem 1 vemos o exemplo das linhas de cabeçalho que tem o host e que faz a requisição HTTP, pedindo que determinada página seja exibida no browser.


GET /diretorio/pagina1.htm HTTP/1.1
Host: www.seusite.com.br
Connection: close
User-agent: Mozilla/2.0
Accept-language: en
Listagem 1. Típica requisição HTTP

Como toda requisição, uma resposta está atrelada a ela após o processamento e a estrutura da resposta HTTP é bem parecida com a da requisição. Primeiro existe a linha de estado, onde se localiza a versão, seguida do código do estado e a frase. As linhas posteriores são bem parecidas, como mostra a Listagem 2, com uma típica resposta HTTP.


HTTP/1.1 200 OK
Connection: close
Date: Wed. 04 Nov 2015 11:31:24 GMT
Server: Apache/2.0 (Unix)
Last-Modified: Wed. 04 Nov 2015 11:31:24 GMT
Content-Length: 2134
Content-type: text/html
 
(dados a serem exibidos no browser)
Listagem 2. Típica resposta HTTP

Para entendermos na prática como funcionam as requisições vamos fazer um teste abrindo o terminal do Linux ou do Mac, ou o prompt do Windows e digite o conteúdo da Listagem 3 para ver o resultado.


telnet google.com 80
 
GET / HTTP1.1
Host: google.com
Listagem 3. Telnet

Como o objetivo do artigo não é o desenvolvimento de um web service e sim o consumo de seus dados, uma API online será utilizada e a requisição será feita para esse servidor. Sua resposta será interpretada, manipulada e exibida na tela da aplicação Android. Está será no formato JSON, que aprenderemos mais na próxima seção.

Arquivo JSON

A API a ser usada é do site randomuser.me, que gera informações randômicas sobre pessoas. Ao ser feita a requisição, um JSON é retornado com as informações a serem trabalhadas. A Listagem 4 mostra a estrutura do arquivo JSON que nos é retornada randomicamente quando o acesso é feito.


{
  "results": [
    {
      "user": {
          "gender": "female",
          "name": {
            "title": "mrs",
            "first": "jetske",
            "last": "van steen"
          },
          "location": {
            "street": "1379 twijnstraat aan de werf",
            "city": "delfzijl",
            "state": "gelderland",
            "zip": 51641
          },
          "email": "jetske.van steen@example.com",
          "username": "heavyladybug279",
          "password": "stoner",
          "salt": "48ECNLyZ",
          "md5": "cb3075f0e4f756bc8b65e366dc24335b",
          "sha1": "c1be88f6529f468c975803fabada5ff4d0feae44",
          "sha256": "8d8e3929260010615d804a6feb0fcc42db6e0cbc6ca613087f456d509dedda01",
          "registered": 916019317,
          "dob": 191584837,
          "phone": "(407)-180-6810",
          "cell": "(043)-907-1117",
          "BSN": "65431107",
          "picture": {
            "large": "https://randomuser.me/api/portraits/women/24.jpg",
            "medium": "https://randomuser.me/api/portraits/med/women/24.jpg",
            "thumbnail": "https://randomuser.me/api/portraits/thumb/women/24.jpg"
          }
      }
    }
  ],
  "nationality": "NL",
  "seed": "0933cf33c4bf81d506",
  "version": "0.7"
} 
Listagem 4. Estrutura arquivo JSON

Note que as várias informações retornadas nesse arquivo JSON podem ser tratadas e exibidas sem problema algum. Mas para isso precisamos entender melhor como será a conexão com o nosso servidor para ter essas informações, como acompanharemos na próxima seção.

Conexão com o servidor

Para captura e interpretação das informações primeiramente analisaremos a classe responsável por se conectar ao servidor e receber esses dados fornecidos. A Listagem 5 mostra o código responsável por isso.


package br.com.home.webservice;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
 
/**
 * Created by allanromanato on 11/4/15.
 */
public class NetworkUtils {
 
    //Responsavel por carregar o Objeto JSON
    public static String getJSONFromAPI(String url){
        String retorno = "";
        try {
           URL apiEnd = new URL(url);
            int codigoResposta;
            HttpURLConnection conexao;
            InputStream is;
 
            conexao = (HttpURLConnection) apiEnd.openConnection();
            conexao.setRequestMethod("GET");
            conexao.setReadTimeout(15000);
            conexao.setConnectTimeout(15000);
            conexao.connect();
 
            codigoResposta = conexao.getResponseCode();
            if(codigoResposta < HttpURLConnection.HTTP_BAD_REQUEST){
                is = conexao.getInputStream();
            }else{
                is = conexao.getErrorStream();
            }
 
            retorno = converterInputStreamToString(is);
            is.close();
            conexao.disconnect();
 
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
 
        return retorno;
    }
 
    private static String converterInputStreamToString(InputStream is){
        StringBuffer buffer = new StringBuffer();
        try{
            BufferedReader br;
            String linha;
 
            br = new BufferedReader(new InputStreamReader(is));
            while((linha = br.readLine())!=null){
                buffer.append(linha);
            }
 
            br.close();
        }catch(IOException e){
            e.printStackTrace();
        }
 
        return buffer.toString();
    }
}
Listagem 5. Código responsável pela camada de rede

Ao analisar o método getJSONFromAPI() nota-se que um objeto do tipo URL é instanciado. Este é responsável por abrir uma conexão com o endereço onde os dados do web service estão localizados. Já o objeto do tipo HttpURLConnection é o responsável por estabelecer a conexão utilizando o protocolo HTTP.

A Listagem 6 mostra mais detalhes sobre esse objeto: repare que um InputStream é declarado e será responsável por receber o Stream do servidor. Por fim um atributo primitivo do tipo int é teclado para receber o código da resposta.


conexao = (HttpURLConnection) apiEnd.openConnection();
conexao.setRequestMethod("GET");
conexao.setReadTimeout(15000);
conexao.setConnectTimeout(15000);
conexao.connect();
Listagem 6. Fragmento de código InputStream

Como o objeto conexão é do tipo InputStream, nota-se que um cast é feito com o objeto apiEnd do tipo URL e o armazena no objeto conexão. Isso significa que o tratamento de conexão aberta será feita com o protocolo HTTP. O tipo de requisição é setado como GET e os tempos de timeout também são setados para 15000 milissegundos. Por fim, a comunicação é estabelecida com o servidor utilizando o protocolo HTTP e o tipo de requisição GET. A Listagem 7 mostra a continuação do método de conexão.


codigoResposta = conexao.getResponseCode();
if(codigoResposta < HttpURLConnection.HTTP_BAD_REQUEST){
  is = conexao.getInputStream();
}else{
 is = conexao.getErrorStream();
}
   
retorno = converterInputStreamToString(is);
is.close();
conexao.disconnect();
Listagem 7. Fragmento de código recebendo dados

O código da resposta é setado no atributo codigoResposta, que é um int. Dentro do bloco if pode-se notar que qualquer código de resposta inferior ao código contido na constante HTTP_BAD_REQUEST, que tem o valor de 400, será dado ao atributo is do tipo InputStream. Se a condição não for válida, um errorStream será atribuído. O retorno recebe o resultado do método, que será analisado em detalhes na Listagem 8, onde o is é encerrado e a conexão fechada. Com isso o método já pode retornar o JSON para ser utilizado na aplicação.


private static String converterInputStreamToString(InputStream is){
  StringBuffer buffer = new StringBuffer();
  try{
    BufferedReader br;
    String linha;

    br = new BufferedReader(new InputStreamReader(is));
    while((linha = br.readLine())!=null){
        buffer.append(linha);
    }

    br.close();
  }catch(IOException e){
      e.printStackTrace();
  }

  return buffer.toString();
}
Listagem 8. Método converterInputStreamToString

Como o retorno obtido anteriormente foi um InputStream, nossa aplicação trabalharia muito melhor com uma String contendo o JSON completo e, como é mais fácil fazer o parse, esse método auxiliar será responsável por converter um InputStream para String.

Um StringBuilder é declarado e será responsável por montar a string JSON a ser retornada. Um BufferedReader também é declarado e será responsável por fazer a leitura do buffer InputStream, juntamente com uma String para receber a linha lida do BufferedReader.

Em seguida o objeto br é inicializado recebendo um novo InputStreamReader, que nada mais é do que a classe lida com o InputStream que foi passado por parâmetro. Um while simples é feito, recebendo a linha atual e, caso seja diferente de nulo, essa linha é inserida no StringBuffer, senão, o loop termina e a String montada é retornada para o método principal.

Vale notar que sempre que quiser um StringBuffer retornado como String é necessário invocar o método toString() dele.

A parte de conexão e recebimento de dados do web wervice está concluída. Agora o que será feito são as transformações necessárias para que as informações recebidas pelo JSON sejam interpretadas e exibidas na tela para o usuário.

Tela do usuário

Um objeto simples foi criado para receber as informações contidas no resultado do web service: trata-se de um POJO simples que contém várias Strings e um Bitmap que será responsável por armazenar uma imagem. A Listagem 9 mostra esse código.


package br.com.home.webservice;
 
import android.graphics.Bitmap;
 
// Created by allanromanato on 11/4/15.
public class PessoaObj {
    private String nome;
    private String sobrenome;
    private String email;
    private String endereco;
    private String cidade;
    private String estado;
    private String username;
    private String senha;
    private String nascimento;
    private String telefone;
    private Bitmap foto;
 
    public String getNome() {
        return nome;
    }
 
    public void setNome(String nome) {
        this.nome = nome;
    }
 
    public String getSobrenome() {
        return sobrenome;
    }
 
    public void setSobrenome(String sobrenome) {
        this.sobrenome = sobrenome;
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public String getEndereco() {
        return endereco;
    }
 
    public void setEndereco(String endereco) {
        this.endereco = endereco;
    }
 
    public String getCidade() {
        return cidade;
    }
 
    public void setCidade(String cidade) {
        this.cidade = cidade;
    }
 
    public String getEstado() {
        return estado;
    }
 
    public void setEstado(String estado) {
        this.estado = estado;
    }
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getSenha() {
        return senha;
    }
 
    public void setSenha(String senha) {
        this.senha = senha;
    }
 
    public String getNascimento() {
        return nascimento;
    }
 
    public void setNascimento(String nascimento) {
        this.nascimento = nascimento;
    }
 
    public String getTelefone() {
        return telefone;
    }
 
    public void setTelefone(String telefone) {
        this.telefone = telefone;
    }
 
    public Bitmap getFoto() {
        return foto;
    }
 
    public void setFoto(Bitmap foto) {
        this.foto = foto;
    }
}
Listagem 9. Código do POJO

Uma outra classe com métodos gerais para executar as operações de parse e o download de imagem foi criada e pode ser observada na Listagem 10.


package br.com.home.webservice;
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
 
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
 
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
 
/**
 * Created by allanromanato on 11/4/15.
 */
public class Utils {
 
    public PessoaObj getInformacao(String end){
        String json;
        PessoaObj retorno;
        json = NetworkUtils.getJSONFromAPI(end);
        Log.i("Resultado", json);
        retorno = parseJson(json);
 
        return retorno;
    }
 
    private PessoaObj parseJson(String json){
      try {
        PessoaObj pessoa = new PessoaObj();

        JSONObject jsonObj = new JSONObject(json);
        JSONArray array = jsonObj.getJSONArray("results");

        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        Date data;

        JSONObject objArray = array.getJSONObject(0);

        JSONObject obj = objArray.getJSONObject("user");
        //Atribui os objetos que estão nas camadas mais altas
        pessoa.setEmail(obj.getString("email"));
        pessoa.setUsername(obj.getString("username"));
        pessoa.setSenha(obj.getString("password"));
        pessoa.setTelefone(obj.getString("phone"));
        data = new Date(obj.getLong("dob")*1000);
        pessoa.setNascimento(sdf.format(data));

        //Nome da pessoa é um objeto, instancia um novo JSONObject
        JSONObject nome = obj.getJSONObject("name");
        pessoa.setNome(nome.getString("first"));
        pessoa.setSobrenome(nome.getString("last"));

        //Endereco tambem é um Objeto
        JSONObject endereco = obj.getJSONObject("location");
        pessoa.setEndereco(endereco.getString("street"));
        pessoa.setEstado(endereco.getString("state"));
        pessoa.setCidade(endereco.getString("city"));

        //Imagem eh um objeto
        JSONObject foto = obj.getJSONObject("picture");
        pessoa.setFoto(baixarImagem(foto.getString("large")));

        return pessoa;
      }catch (JSONException e){
          e.printStackTrace();
          return null;
      }
  }
 
    private Bitmap baixarImagem(String url) {
      try{
          URL endereco;
          InputStream inputStream;
          Bitmap imagem; endereco = new URL(url);
          inputStream = endereco.openStream();
          imagem = BitmapFactory.decodeStream(inputStream);
          inputStream.close();
          return imagem;
      }catch (IOException e) {
          e.printStackTrace();
          return null;
      }
  }
}
Listagem 10. Classe responsável por operações gerais

A classe mostrada na Listagem 10 está dividida em três métodos: getInformacao, parseJson e baixarImagem.

O método getInformacao retornará um objeto do tipo PessoaObj, que será utilizado para popular a interface com o usuário. Esse método é público, pois é chamado fora dessa classe. Nota-se que é feita uma chamada ao método estático getJSONFromAPI() (já analisado na Listagem 5), e seu retorno é armazenado em um objeto do tipo String. Um log é invocado para que o desenvolvedor possa ver se as informações estão chegando corretamente do servidor.

O método parseJson armazena o resultado do método getJSONFromAPI() em um objeto do tipo PessoaObj e este é retornado para quem o invocou. Esse método é privado, pois ele será acessado apenas dentro de sua classe de origem. É importante notar também que ele recebe uma String com o JSON completo recebido do servidor.

O que precisamos fazer agora é o parse, ou seja, capturar nesse objeto JSON as informações que serão exibidas na interface com o usuário. Serão utilizadas três classes que estão embutidas na API do Android para que o parse seja executado: JSONArray, JSONObject, JSONException. Essas três classes estão localizadas no pacote org.json.

Ao analisar o objeto JSON contido na Listagem 4 pode-se observar um padrão na criação e, a partir deste deve ser feito o parse. A Listagem 11 mostra em detalhes algumas chamadas nos níveis mais superiores do objeto JSON.


JSONObject jsonObj = new JSONObject(json);
JSONArray array = jsonObj.getJSONArray("results");
   
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Date data;
   
JSONObject objArray = array.getJSONObject(0);
   
JSONObject obj = objArray.getJSONObject("user");
//Atribui os objetos que estão nas camadas mais altas
pessoa.setEmail(obj.getString("email"));
pessoa.setUsername(obj.getString("username"));
pessoa.setSenha(obj.getString("password"));
pessoa.setTelefone(obj.getString("phone"));
data = new Date(obj.getLong("dob")*1000);
pessoa.setNascimento(sdf.format(data));
Listagem 11. Código para fazer parse dos níveis mais altos

Primeiramente um JSONObject é instanciado passando por parâmetro a String que contém o objeto. Logo abaixo o Array chamado results é instanciado como um JSONArray. Um SimpleDateFormat é instanciado para que se possa trabalhar com a data de nascimento, o mesmo com o objeto Data. Outro JSONObject é declarado e todos os objetos que estão contidos na primeira posição desse array de JSON são colocados nele. Notem que esse array tem apenas uma posição, então é acessado pela posição 0. Agora o que é feito é atribuir o JSONObject e o conteúdo do objeto user.

Por fim, os métodos getString e getLong são chamados para as informações serem carregadas para os campos do objeto PessoaObj.

Notem a seguinte linha:


data = new Date(obj.getLong(“dob”)*1000);

Isso acontece porque a data é dada em formato EPOCH, ou seja, a quantidade de segundos passados do dia 1 de janeiro de 1970 até a data que se quer ver, então a quantidade é multiplicada por 1000, pois será trabalhado com milissegundos para que a classe Date possa exibi-la como uma data. Veja que o SimpleDateFormat formata para dia/mês/ano.


//Nome da pessoa é um objeto, instancia um novo JSONObject
JSONObject nome = obj.getJSONObject("name");
pessoa.setNome(nome.getString("first"));
pessoa.setSobrenome(nome.getString("last"));
   
//Endereco tambem é um Objeto
JSONObject endereco = obj.getJSONObject("location");
pessoa.setEndereco(endereco.getString("street"));
pessoa.setEstado(endereco.getString("state"));
pessoa.setCidade(endereco.getString("city"));
   
//Imagem eh um objeto
JSONObject foto = obj.getJSONObject("picture");
pessoa.setFoto(baixarImagem(foto.getString("large")));
   
return pessoa;
Listagem 12. Código para fazer parse em níveis inferiores

Nota-se na Listagem 12 que novos objetos JSON devem ser instanciados, isso ocorre por estarem em subníveis. Por exemplo, dentro do nível user existe o subnível name, que contém as informações de nome e sobrenome, e o mesmo ocorre com endereço. Nesse fragmento de código não tem muito segredo, pois apenas é preciso saber quais são os níveis e subníveis e ir instanciando. Seus conteúdos também são carregados e armazenados nos atributos do objeto PessoaObj. Por fim, pode-se notar que ao carregar o atributo foto, o método baixarFoto() é invocado. Isso acontece porque o JSON traz apenas o endereço que a foto está localizada, portanto, deve-se fazer o download dela, para exibir na aplicação. A Listagem 13 mostra o método responsável por fazer o download da imagem.


private Bitmap baixarImagem(String url) {
 try{
    URL endereco;
    InputStream inputStream;
    Bitmap imagem; endereco = new URL(url);
    inputStream = endereco.openStream();
    imagem = BitmapFactory.decodeStream(inputStream);
    inputStream.close();
    return imagem;
  }catch (IOException e) {
    e.printStackTrace();
    return null;
  }
  
}
Listagem 13. Método baixarFoto()

Esse é um método privado bem simples responsável por converter um InputStream em imagem do tipo Bitmap utilizando ao método estático decodeStream da classe BitmapFactory.

Com isso a classe utilitária está finalizada, lembrando que ela é responsável apenas por lidar com as informações carregadas da API do randomuser.me. A seguir veremos como enviar essas informações para o usuário através da activity correspondente.

Activity do usuário

Será estudado agora a activity responsável por controlar a interface com usuário e fazer o link com o XML de layout. Utilizaremos o RelativeLayout, que é bem simples e tem como estrutura o código exibido na Listagem 14.


<RelativeLayout xmlns:android="http://schemas.
android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent"
  android:layout_height="match_parent" 
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  android:paddingBottom="@dimen/activity_vertical_margin" 
  tools:context=".MainActivity">

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textAppearance="?android:attr/textAppearanceLarge"
      android:text="RandomUser"
      android:id="@+id/textView"
      android:layout_alignParentTop="true"
      android:layout_centerHorizontal="true" />

  <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/imageView"
      android:layout_below="@+id/textView"
      android:layout_alignLeft="@+id/textView"
      android:layout_marginTop="30dp"
      android:layout_alignParentLeft="true"/>

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView2"
      android:layout_below="@+id/textView3"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView3"
      android:layout_below="@+id/textView4"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView4"
      android:layout_below="@+id/textView7"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView5"
      android:layout_below="@+id/imageView"
      android:layout_centerHorizontal="true"
      android:layout_marginTop="71dp"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Nome:"
      android:id="@+id/textView6"
      android:layout_marginLeft="24dp"
      android:layout_marginStart="24dp"
      android:layout_alignBaseline="@+id/textView5"
      android:layout_alignBottom="@+id/textView5"
      android:layout_alignParentLeft="true"
      android:layout_alignParentStart="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView7"
      android:layout_below="@+id/textView8"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView8"
      android:layout_below="@+id/textView11"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView9"
      android:layout_below="@+id/textView10"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView10"
      android:layout_below="@+id/textView2"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView11"
      android:layout_below="@+id/textView5"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:id="@+id/textView12"
      android:layout_below="@+id/textView9"
      android:layout_centerHorizontal="true"
      android:layout_alignParentRight="true" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Sobrenome:"
      android:id="@+id/textView13"
      android:layout_below="@+id/textView6"
      android:layout_alignParentLeft="true"
      android:layout_marginLeft="22dp" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Email:"
      android:id="@+id/textView14"
      android:layout_below="@+id/textView13"
      android:layout_alignLeft="@+id/textView13"
      android:layout_alignStart="@+id/textView13" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Endereço:"
      android:id="@+id/textView15"
      android:layout_below="@+id/textView14"
      android:layout_alignLeft="@+id/textView14"
      android:layout_alignStart="@+id/textView14" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Cidade:"
      android:id="@+id/textView16"
      android:layout_below="@+id/textView15"
      android:layout_alignLeft="@+id/textView15"
      android:layout_alignStart="@+id/textView15" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Estado:"
      android:id="@+id/textView17"
      android:layout_below="@+id/textView16"
      android:layout_alignLeft="@+id/textView14"
      android:layout_alignStart="@+id/textView14" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Username:"
      android:id="@+id/textView18"
      android:layout_below="@+id/textView17"
      android:layout_alignLeft="@+id/textView16"
      android:layout_alignStart="@+id/textView16" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Senha:"
      android:id="@+id/textView19"
      android:layout_below="@+id/textView18"
      android:layout_alignLeft="@+id/textView18"
      android:layout_alignStart="@+id/textView18" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Nascimento:"
      android:id="@+id/textView20"
      android:layout_below="@+id/textView19"
      android:layout_alignLeft="@+id/textView19"
      android:layout_alignStart="@+id/textView19" />

  <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="Telefone:"
      android:id="@+id/textView21"
      android:layout_alignTop="@+id/textView12"
      android:layout_alignLeft="@+id/textView20"
      android:layout_alignStart="@+id/textView20" />
</RelativeLayout>
Listagem 14. Arquivo de layout

Repare que os elementos contidos nesse layout sempre estão alinhados relativamente a outros. Repare também que identificamos as posições dos elementos utilizando ids dos outros. A Listagem 15 mostra efetivamente o código da Activity, que será analisada.


package br.com.home.webservice;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;


public class MainActivity extends Activity {
  private TextView nome;
  private TextView sobrenome;
  private TextView email;
  private TextView endereco;
  private TextView cidade;
  private TextView estado;
  private TextView username;
  private TextView senha;
  private TextView nascimento;
  private TextView telefone;
  private ImageView foto;
  private ProgressDialog load;

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

      GetJson download = new GetJson();

      nome = (TextView)findViewById(R.id.textView5);
      sobrenome = (TextView)findViewById(R.id.textView11);
      email = (TextView)findViewById(R.id.textView8);
      endereco = (TextView)findViewById(R.id.textView7);
      cidade = (TextView)findViewById(R.id.textView4);
      estado = (TextView)findViewById(R.id.textView3);
      username = (TextView)findViewById(R.id.textView2);
      senha = (TextView)findViewById(R.id.textView10);
      nascimento = (TextView)findViewById(R.id.textView9);
      telefone = (TextView)findViewById(R.id.textView12);
      foto = (ImageView)findViewById(R.id.imageView);

      //Chama Async Task
      download.execute();

  }

  private class GetJson extends AsyncTask<Void, Void, PessoaObj> {

      @Override
      protected void onPreExecute(){
          load = ProgressDialog.show(MainActivity.this, 
          "Por favor Aguarde ...", "Recuperando Informações do Servidor...");
      }

      @Override
       protected PessoaObj doInBackground(Void... params) {
          Utils util = new Utils();

          return util.getInformacao("https://randomuser.me/api/0.7");
      }

      @Override
      protected void onPostExecute(PessoaObj pessoa){
          nome.setText(pessoa.getNome().substring(0,1)
          .toUpperCase()+pessoa.getNome().substring(1));
          sobrenome.setText(pessoa.getSobrenome().substring(
          0,1).toUpperCase()+pessoa.getSobrenome().substring(1));
          email.setText(pessoa.getEmail());
          endereco.setText(pessoa.getEndereco());
          cidade.setText(pessoa.getCidade().substring(0,1)
          .toUpperCase()+pessoa.getCidade().substring(1));
          estado.setText(pessoa.getEstado());
          username.setText(pessoa.getUsername());
          senha.setText(pessoa.getSenha());
          nascimento.setText(pessoa.getNascimento());
          telefone.setText(pessoa.getTelefone());
          foto.setImageBitmap(pessoa.getFoto());
          load.dismiss();
      }
  }
}
Listagem 15. Código da Activity Principal

Nota-se na Listagem 15 que o método onCreate é bem simples, pois apenas faz as instanciações necessárias para fazer a ponte entre os campos no XML e a aplicação utilizando o método findViewById, e ao final é feita a chamada de um AsyncTask.

Se não fizermos essa chamada nada irá funcionar. Pode-se utilizar qualquer técnica que separe o processamento, tipo threads, mas o Async Task já implementa tudo isso, facilitando o seu código.

Se um processamento paralelo não for utilizado, uma exceção do tipo NetworkOnMainThreadException é lançada. Existe uma forma de solucionar esse problema, mas é uma solução péssima do ponto de vista dos desenvolvedores Android e é mais usada como paliativo para testar a aplicação antes de separar o processamento. Assim, o código a seguir consiste em mudar as políticas do Android com relação a sua aplicação:


StrictMode.ThreadPolicy policy = newStrictMode.ThreadPolicy.Builder()
.permitAll().build();
.setThreadPolicy(policy);

Ao utilizar isso, podemos dizer ao Android que sua aplicação poderá utilizar operações de rede na Thread principal, porém isso é péssimo. Imagine uma situação em que as operações de rede estão ocorrendo na thread principal e o servidor para de responder: em um determinado tempo sua aplicação será encerrada ou o usuário achará que está tudo travado, mas ela apenas está esperando a resposta do servidor. Não utilizem isso para sistemas reais, pois o Async Task está aí e resolve esses problemas sem causar danos na navegação.

Dentro do Async Task três métodos são implementados: o método protected void onPreExecute(), que será o responsável por exibir na tela um modal de progresso, mostrando ao usuário que a requisição dele está em execução; o método protected PessoaObj doInBackground(Void... params), que irá ser o responsável por executar a primeira chamada que fará todo o processamento, download, parse e download de foto em uma thread separada; e por fim o método protected void onPostExecute(PessoaObj pessoa), que recebe como parâmetro o retorno do método doInBackground, e ele é responsável por colocar na tela todas as informações contidas dentro do objeto PessoaObj e desligar o modal de progresso. Vocês podem notar que em alguns campos o método subString é chamado, mas isso é apenas para colocar a primeira letra em maiúsculo.

Por fim, não se pode esquecer do arquivo de manifesto, pois como a aplicação acessará um Web Service remoto, a permissão de acesso à internet deve ser dada no manifest, segundo o código da Listagem 16.


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="br.com.home.webservice" >
  <uses-permission android:name="android.permission.INTERNET"/>
  <application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:theme="@style/AppTheme" >
      <activity
          android:name=".MainActivity"
          android:label="@string/app_name" >
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />

              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
  </application>

</manifest>
Listagem 16. Android Manifest

Na Figura 1 podem ser acompanhados os resultados da implementação descrita.

Resultado da Aplicação
Figura 1. Resultado da Aplicação

Pode-se aprender nesse artigo como conectar-se a um Web Service e consumir suas informações de forma bem simples e direta, apenas sendo necessários alguns conceitos de redes e de protocolo HTTP. Usamos nesse exemplo os arquivos JSON, pois eles ocupam menos espaço que o XML e são altamente recomendados para o uso em dispositivos móveis.

Confira também