Conexão HTTP e Json no Android!

Introdução

É muito raro encontrar aplicações para dispositivos móveis que não fazem uso de alguma conexão com um servidor web. Esse cenário ocorre por um simples fato: Dispositivos móveis não são eternos e não são interligados.
Os dados que estão em um celular podem ser perdidos até com certa facilidade ( o celular pode quebrar, o dono pode perde-lo, pode ser roubado, enfim varias coisas podem acontecer).
Também existe o fato que em diversas situações, as informações contidas nele devem ser compartilhadas entre múltiplos dispositivos ou sistemas o que não é possível sem uma conexão com um servidor Web.
Por isso os dados que ficam armazenados em um celular geralmente devem ser categorizados como dados provisórios.
Logo, é fundamental que os dados realmente importantes para o sistema, ou aqueles que devem ser compartilhados, sejam mantidos em um servidor web para que não sejam perdidos quando o usuário resolver trocar de telefone por exemplo.

Nesse post vamos realizar um exemplo de comunicação entre uma aplicação Android e um Sistema Web. Abordaremos as seguintes tecnologias:

JSON – O json é um padrão de formato de dados para comunicação entre sistemas(Assim como o XML). Com o JSON conseguimos executar ações como:
Transformar objetos em uma String para poder trafega-lo ao sistema de destino. Chegando la o sistema que recebeu a String JSON a transforma novamente em um objeto e realiza o processamento que desejar. O framework que usaremos para serializar e desserializar os dados será o Jackson ( Link do Site do Jackson ).

Servlet –Estrutura do Java EE para tratar requisições HTTP.

Começando um projeto de exemplo

No nosso aplicativo de exemplo criaremos três projetos.

Um projeto Android, um projeto Java WEB onde estará presente o nosso Servlet e por último um projeto Java SE que manterá os dados que serão utilizados pelas duas aplicações Web e Mobile. Essa é uma arquitetura comumente utilizada em sistemas mobile.
Começaremos nosso exemplo criando o projeto compartilhado, o chamaremos de ServiceEntidade. Como o próprio nome indica, esse projeto ficará com as entidades que serão trafegadas entre as aplicações Web e Mobile.
No eclipse para criar um projeto Java SE, clique em File -> New -> Java Project. Com o projeto criado vamos criar a nossa classe que será trafegada entre as aplicações. A chamaremos de Cidadao e se trata de um Pojo uma tradicional entidade de negócio. Como o objetivo é sermos o mais simples possível essa classe possui somente dois atributos: nome (String) e idade(int).
Logo abaixo segue o código dessa classe:

public class Cidadao {

	private String nome;
	private int idade;
	
	
	public Cidadao() {
		
	}
	
	public Cidadao(String nome, int idade) {
		super();
		this.nome = nome;
		this.idade = idade;
	}
	
	public String getNome() {
		return nome;
	}
	public void setNome(String nome) {
		this.nome = nome;
	}
	public int getIdade() {
		return idade;
	}
	public void setIdade(int idade) {
		this.idade = idade;
	}

}

Agora criaremos o projeto Java Web que terá o nome de ServiceWeb, para cria-lo no eclipse clique em file->new->Other->Web->Dynamic Web Project. Com o projeto criado, crie a classe FrontController que vai estender a classe HttpServlet. Podemos ver que a classe HttpServlet disponibiliza uma série de métodos para tratar requisições. Nesse post utilizaremos o método POST representado pelo método doPost. Ambos os métodos recebem como parâmetro as classes HttpServletRequest e HttpServletResponse, responsáveis respectivamente por tratar os dados que chegaram da requisição e os dados que serão devolvidos ao cliente da solicitação.
O que o nosso FrontController vai realizar é muito simples. Ele vai receber um objeto Cidadão em formato Json que vai chegar através de uma requisição do dispositivo móvel. Em seguida vai transforma-lo novamente em Objeto cidadão, para alterar o atributo idade para 25 e envia-lo novamente para o dispositivo móvel como resposta daquela requisição.
Vejamos o código:

public class FrontController extends HttpServlet {

	private static final long serialVersionUID = 1L;

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)	throws ServletException, IOException {		
		super.doPost(req, resp);
		processRequest(req, resp);
	}

	public void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("Processando requisição - Front Controller UM");
		
		String requestData = processRequest(request);	 /* A String JSOn enviada pelo dispositivo móvel */	
		System.out.println("Request Data" + requestData);
		
		Cidadao cidadao = DataSerializer.getInstance().toObject(requestData, Cidadao.class);
		cidadao.setIdade(25);
		
		String responseData = DataSerializer.getInstance().toJson(cidadao);

		 PrintWriter out = response.getWriter();
		 out.write(responseData);
	}
	
	private String processRequest(HttpServletRequest request) {
		 StringBuffer jb = new StringBuffer();
		  String line = null;
		  try {
		    BufferedReader reader = request.getReader();
		    while ((line = reader.readLine()) != null)
		      jb.append(line);
		  } catch (Exception e) { e.printStackTrace(); }
		  
		  return jb.toString();

	}

}

Obs: Este post me ajudou a saber como tratar a requisição:
Depois de criar essa estrutura você poderá executa-la em algum contêiner como o TomCat, e acessa-lo através do endereço:
http://localhost:8080/ServiceWeb/FrontController
Analisando os detalhes de implementação do nosso FrontController, veja que conseguimos serializar (toJson) e desserializar(toObject) dados Json através da classe DataSerializer. Essa classe, se trata de uma classe utilitária que encapsula as chamadas do nosso Framework Jackson.

Obs: O Jackson é um framework bem popular para transformar objetos Java em uma String no formato JSON e vice e versa.
O jar do Jackson e mais informações sobre o mesmo podem ser encontrados em seu site, segue link: http://jackson.codehaus.org/

Podemos vê-la abaixo:

public class DataSerializer {

	
	private static DataSerializer instance;
	ObjectMapper objectMapper = null;
	
	private DataSerializer() {
		objectMapper = new ObjectMapper();
	}
	
	public static DataSerializer getInstance() {
		if(instance == null)
			instance = new DataSerializer();
		
		return instance;
	}

	public String toJson(Object content) throws IOException {		
		objectMapper.getSerializationConfig().setSerializationInclusion(Inclusion.NON_NULL);		
		return objectMapper.writeValueAsString(content);
	}
		
	public<T>  T toObject(String json, Class targetClass) throws JsonParseException, JsonMappingException, IOException {
		return (T) objectMapper.readValue(json, targetClass);	
	}


}

Essa estrutura será utilizada pelas duas partes(Mobile e Web), por isso é uma boa ideia que ela seja criada dentro do projeto ServiceEntidade, pois ele esta vinculado aos dois projetos. Você vinculará os projetos através do Java Build Path (botão direito no projeto desejado -> properties -> java build path) nesse local também será inserido os arquivos JAR’s.
Agora que já temos um Servlet capaz de ler os dados enviados do dispositivo móvel e enviar uma resposta para aquela requisição. Chegou o momento de criar a parte do Android. A parte que iniciará as requisições. Então vamos criar um projeto Android e o chamaremos de ServiceAndroid fazemos isso no eclipse através dos passos File -> New -> Android Project. Quando um projeto Android no eclipse é criado ele já deixa para o desenvolvedor uma Activity e um layout com um TextView. Vamos aproveitar esse layout,para exibir a resposta do servido no TextView. Também devemos acrescentar um Button para enviar a requisição.
O layout deverá ficar assim:


<LinearLayout 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" >

    <TextView
        android:id="@+id/textViewStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"        
        android:text="@string/hello_world"
        />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"        
        android:text="Button" 
        android:onClick="onClickConnect"/>

</LinearLayout>

E será vinculada a nossa Activity que chamamos de MobileConnectionActivity. A nossa Activity terá uma constante que representa a URL que usaremos para conectarmos ao servidor. Também terá o método onClickConnect que será acionado após o usuário apertar o botão que criamos no layout. Esse método executará uma requisição do tipo Post para o Servlet FrontController que criamos anteriormente.
O código pode ser visto logo em seguida:

public class MobileConnectionActivity extends Activity {

 
	private static final String URL = "http://192.168.0.11:8080/ServiceMobile/FrontController"; // Emulador


	TextView textView = null;
	
	@Override
	public void onCreate(Bundle savedInstanceState) { 
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_mobile_connection);
		textView = (TextView)findViewById(R.id.textViewStatus);
	}

	public void onClickConnect(View view) {
		new Thread() {
			@Override
			public void run() {
				super.run();
				Log.i("Iniciando", "Initializing");
				SynchronousHttpConnection httpConnection = new SynchronousHttpConnection();
				try {
				    Cidadao cidadao = new Cidadao("Leonardo", 22);
					String data = DataSerializer.toJson(cidadao);	
					String response = httpConnection.post(URL,data);
					Log.i("RESPONSE", "Resposta: " + response);
					atualizeTextView(response);
				} catch (IllegalStateException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} 
			}
		}.start();

	}
	
	private void atualizeTextView(final String text) {
		runOnUiThread(new Runnable() {
			
			@Override
			public void run() {
			   textView.setText(text);
			}
		});
	}
}

Analisando o código podemos perceber que também utilizamos a estrutura DataSerializer para serializar e desserializar a classe Cidadão no formato JSON, para em seguida utilizarmos o método post da classe SynchronousHttpConnection para realizar a requisição HTTP.

OBS: A requisição HTTP no Android é SEMPRE executada por uma Thread separada. Isso acontece porque se trata de um processamento mais demorado e o Android não permite que ele seja executado junto a Thread principal responsável pela Interface Gráfica. Se você não respeitar essa regra irá receber um erro bastante comum no Android o ANR (Application Not Respondig)

Voltando ao método post da classe SynchronousHttpConnection podemos ver que ele retorna uma String que se trata da resposta do servidor, exibiremos essa String no nosso TextView através do método que criamos atualizeTextView que faz uso do método runOnUiThread da própria Activity. Isto é feito porque o Android possui uma Thread separada para atualizar a interface gráfica, o método runOnUiThread é uma das formas para acessar essa Thread. Se tentássemos atualizar o TextView direto da nossa Thread referente a chamada do servidor uma excessão seria lançada.
A classe SynchronousHttpConnection pode ser vista logo abaixo.
Veja que ela encapsula as classes de conexão HTTP providas pelo Android de modo que esta preparada para realizar requisições HTTP nos mais variados métodos.

public class SynchronousHttpConnection {

	public static final int DID_START = 0;
	public static final int DID_ERROR = 1;
	public static final int DID_SUCCEED = 2;

	private static final int GET = 0;
	private static final int POST = 1;
	private static final int PUT = 2;
	private static final int DELETE = 3;

	public SynchronousHttpConnection() {				
	}

	public String get(String url) throws IllegalStateException, IOException {
		return executeHTTPConnection(GET, url, null);
	}

	public String post(String url, String data) throws IllegalStateException, IOException {
		return executeHTTPConnection(POST, url, data);
	}

	public String put(String url, String data) throws IllegalStateException, IOException {
		return executeHTTPConnection(PUT, url, data);
	}

	public String delete(String url) throws IllegalStateException, IOException {
		return executeHTTPConnection(DELETE, url, null);
	}

	public Bitmap bitmap(String url) throws IllegalStateException, IOException {
		return executeHTTPConnectionBitmap(url);
	}


	private String executeHTTPConnection(int method, String url, String data) throws IllegalStateException, IOException {
		HttpClient httpClient = new DefaultHttpClient();
		HttpConnectionParams.setSoTimeout(httpClient.getParams(), 25000);
		HttpResponse response = null;
		switch (method) {
		case GET:
			HttpGet httpGet = new HttpGet(url);
			response = httpClient.execute(httpGet);
			break;
		case POST:
			HttpPost httpPost = new HttpPost(url);
			httpPost.setEntity(new StringEntity(data));
			response = httpClient.execute(httpPost);
			break;
		case PUT:
			HttpPut httpPut = new HttpPut(url);
			httpPut.setEntity(new StringEntity(data));
			response = httpClient.execute(httpPut);
			break;
		case DELETE:
			response = httpClient.execute(new HttpDelete(url));
			break;
		default:
			throw new IllegalArgumentException("Unknown Request.");
		}  

		return processResponse(response.getEntity());

	}

	private Bitmap executeHTTPConnectionBitmap(String url) throws IllegalStateException, IOException {
		HttpClient httpClient = new DefaultHttpClient();
		HttpConnectionParams.setSoTimeout(httpClient.getParams(), 25000);
		HttpResponse response = httpClient.execute(new HttpGet(url));		
		return processBitmapEntity(response.getEntity());
	}

	private String processResponse(HttpEntity entity) throws IllegalStateException,IOException {
                String jsonText = EntityUtils.toString(entity, HTTP.UTF_8);
		return jsonText;
	}

	private Bitmap processBitmapEntity(HttpEntity entity) throws IOException {
		BufferedHttpEntity bufHttpEntity = new BufferedHttpEntity(entity);
		Bitmap bm = BitmapFactory.decodeStream(bufHttpEntity.getContent());
		return bm;
	}

}

Com este post espero ter ajudado a resolver um dos principais problemas em desenvolvimento para dispositivos móveis. A conexão com o servidor web.

Abraço a todos.

Anúncios

, ,

  1. #1 by Handrey CK on Outubro 5, 2015 - 7:05 pm

    cara vc tem no android estudio ?

  2. #2 by Jhonatan on Abril 2, 2014 - 7:31 pm

    Leonardo, quando tento criar a classe FrontController extends HttpServlet da erro, na HttpServlet como resolvo isso?

  3. #4 by alanperius on Dezembro 11, 2013 - 2:17 pm

    Artigo muito útil, mas mal explicado algumas coisas.. principalmente quem nunca mexeu com requisições http..

    por exemplo a classe DateSerializer:
    Essa classe, se trata de uma classe utilitária que encapsula as chamadas do nosso Framework Jackson.

    como assim? preciso add alguma lib desse framework?

    abraços e isso é apenas uma critica construtiva não leve a mal.

    • #5 by Leonardo Casasanta on Dezembro 11, 2013 - 4:10 pm

      Ola Alan,

      Obrigado pelo comentário, vou melhorar essa parte.

      Já tentando te ajudar, o Jackson é um framework que tem o objetivo de transformar dados em uma String no formato JSON, geralmente utilizado em integração de sistemas.
      Você vai precisar sim de um jar para colocar na pasta lib do seu projeto Android.
      Você encontra o Jar e mais informações no site do próprio Jackson: http://jackson.codehaus.org/

      Espero ter ajudado,

      Abraço.

      • #6 by alanperius on Dezembro 11, 2013 - 5:29 pm

        Opa.. Valeu aí!

        era isso mesmo que faltou.. esse .jar, os erros sumiram agora.

        abraço!

  4. #7 by kelman on Março 16, 2013 - 9:39 pm

    Excelente exemplo
    mas da sempre erro na classe DataSerializer…tu podias disponibilizar o exemplo para donwload porque nem sei quais lib importar….e s possivel uma imagem do projeto funcionado…isso ira me ajudar muito na minha monografia…Grato

  5. #8 by Gabriel on Março 11, 2013 - 1:53 pm

    não consigo fazer o DataSerializer funcionar..
    erro: No exception of type JsonMappingException can be thrown

  6. #9 by Paulo roberto on Fevereiro 28, 2013 - 6:55 pm

    Você desenvolveu em qual versão do eclipse? Eu estou tentando fazer funcionar aqui mas não consegui ainda. Se puder colocar os projetos para serem baixados seria interessante. Obrigado.

    • #10 by Leonardo Casasanta on Fevereiro 28, 2013 - 7:52 pm

      Desenvolvi utilizando o eclipse JUNO.

      Seu problema é de ambiente ou alguma excessão que esta acontecendo no código?

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão / Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão / Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão / Alterar )

Google+ photo

Está a comentar usando a sua conta Google+ Terminar Sessão / Alterar )

Connecting to %s

%d bloggers like this: