Ler uma imagem via HTTP


A comunicação HTTP no Android deve ser feita dentro de uma thread para evitar que a aplicação fique “presa” à espera da resposta do servidor, não permitindo, durante esse tempo, que o utilizador interaja com a mesma. No entanto, neste artigo vamos solicitar uma imagem a um servidor Web dentro da thread principal. É conveniente que a imagem seja pequena (tenha apenas alguns kb) para que a resposta do servidor não faça a aplicação terminar por inatividade. No próximo artigo sobre comunicação HTTP falaremos da criação de uma thread com a classe AsyncTask para implementar uma comunicação HTTP num ambiente mais seguro.

Este exemplo destina-se a apresentar uma classe de comunicação HTTP que permite enviar pedidos via GET ou via POST a um servidor. Para o efeito, são usadas duas classes: HttpRequest e HttpData. A primeira implementa os métodos de invocação dos comandos GET e POST. A segunda gere a resposta do servidor que pode ser um bloco de texto ou um ficheiro binário.

Segue-se o código da classe HttpRequest:

package com.se.login;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

public class HttpRequest {
    public HttpClient httpclient;

    public HttpRequest() {
        HttpParams httpParameters = new BasicHttpParams();
        // Set the timeout in milliseconds until a connection is established.
        // The default value is zero, that means the timeout is not used.
        int timeoutConnection = 30000;
        HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
        // Set the default socket timeout (SO_TIMEOUT)
        // in milliseconds which is the timeout for waiting for data.
        int timeoutSocket = 60000;
        HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

        httpclient = new DefaultHttpClient(httpParameters);
        httpclient.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
    }

    // Faz um pedido GET
    public HttpData get(String url){
        HttpData dados = new HttpData();
        HttpGet request = new HttpGet();
        try {
            request.setURI(new URI(url));
            HttpResponse resp = httpclient.execute(request);
            dados.set(resp);
        } catch (URISyntaxException e1) {
            e1.printStackTrace();
        } catch (ClientProtocolException e1) {
            e1.printStackTrace();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return dados;
    }
    public HttpData getImage(String url, List<NameValuePair> vars) {
        HttpData dados = new HttpData();
        HttpPost request = new HttpPost(url);
        try {
            request.setEntity(new UrlEncodedFormEntity(vars));
            HttpResponse resp = httpclient.execute(request);
            dados.setImage(resp);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dados;
    }

    // Faz um pedido via POST, com variáveis
    public HttpData post(String url, List<NameValuePair> vars){
        HttpData dados = new HttpData();
        HttpPost request = new HttpPost(url);
        try {
            request.setEntity(new UrlEncodedFormEntity(vars));
            HttpResponse resp = httpclient.execute(request);
            dados.set(resp);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dados;
    }
}

A classe HttpData tem o seguinte conteúdo:

package com.se.login;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Hashtable;

import org.apache.http.Header;
import org.apache.http.HttpResponse;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

public class HttpData {
    public String content;
    public Hashtable<String,String> cookies;
    public Hashtable<String,String> headers;
    public int statusCode;
    public Bitmap imagem;

    public HttpData() {
        content = "";
        imagem = null;
        cookies = new Hashtable<String,String>();
        headers = new Hashtable<String,String>();
        statusCode = 0;
    }

    public void set(HttpResponse resp) {
        if(resp == null) return;
        BufferedReader in = null;
        try {
            in = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
                if(sb.length() != 0) sb.append(NL);
                sb.append(line);
            }
            in.close();
            content = sb.toString();
            if(content.startsWith("")) content = content.substring(3);
            Log.d("LOG", "HttpData.set(), tamanho de content: " + content.length());
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        Header[] hds = resp.getAllHeaders();
        for(Header h: hds) {
            headers.put(h.getName(), h.getValue());
            if (h.getName().equals("set-cookie"))
                cookies.put(h.getName(), h.getValue());

        }
        statusCode = resp.getStatusLine().getStatusCode();
        Log.d("NET", "statusCode = " + statusCode);
    }

    public void setImage(HttpResponse resp) {
        if(resp == null) return;
        InputStream in = null;
        try {
            in = resp.getEntity().getContent();
            imagem = BitmapFactory.decodeStream(in);
            Log.d("LOG", "HttpData.setImage(), tamanho de content: " + content.length());
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        Header[] hds = resp.getAllHeaders();
        for(Header h: hds) {
            headers.put(h.getName(), h.getValue());
            if (h.getName().equals("set-cookie"))
                cookies.put(h.getName(), h.getValue());

        }
        statusCode = resp.getStatusLine().getStatusCode();
        Log.d("NET", "statusCode = " + statusCode);
    }
}

Esta aplicação tem um layout gráfico relativamente simples:

  • um campo de texto para se colocar o endereço Web de uma imagem
  • um botão para acionar o pedido
  • um campo do tipo ImageView para apresentar a imagem lida da Net.

Segue-se o layout gráfico :

<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"
	tools:context=".MainActivity"
	android:orientation="vertical"
	>
	<TextView
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="URL da imagem:"
		/>
	<EditText
		android:id="@+id/url"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:textSize="20sp"
		/>
	<Button
		android:id="@+id/botao"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="Ler Imagem"
		android:textSize="20sp"
		/>
	<ImageView
		android:id="@+id/imagem"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		/>
</LinearLayout>

Para que a aplicação possa comunicar via HTTP, é necessário adicionar à aplicação a permissão de acesso à Web. No ficheiro Android Manifest adicionar o código seguinte antes da marca <application…>:

<uses-permission android:name="android.permission.INTERNET" />

O layout fica com o aspeto seguinte:
imagem

Por fim, a aplicação principal tem o código abaixo:

package com.example.imagem;

import android.os.Bundle;
import android.os.StrictMode;
import android.os.StrictMode.ThreadPolicy;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;

public class MainActivity extends Activity {ImageView imagem;
	EditText url;
	Button botao;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		ThreadPolicy tp = ThreadPolicy.LAX;
		StrictMode.setThreadPolicy(tp);
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		imagem = (ImageView) findViewById(R.id.imagem);
		url = (EditText) findViewById(R.id.url);
		botao = (Button) findViewById(R.id.botao);

		botao.setOnClickListener( new View.OnClickListener() {
			public void onClick(View v) {
				String sUrl = url.getText().toString();
				HttpRequest req = new HttpRequest();
				HttpData dados = req.getImage(sUrl);
				imagem.setImageBitmap(dados.imagem);
			}
		});
	}
}

As duas primeiras linhas da função onCreate servem para permitir a comunicação HTTP dentro da thread principal. São as seguintes:

ThreadPolicy tp = ThreadPolicy.LAX;
StrictMode.setThreadPolicy(tp);

Nas primeiras versões do sistema operativo Android era possível executar um pedido HTTP dentro da thread principal. Atualmente isso gera uma exceção do tipo android.os.NetworkOnMainThreadException. Estas duas linhas permitem uma política  de utilização de threads mais permissiva e evitam a ocorrência da exceção. Num artigo próximo, esta situação será resolvida invocando o pedido HTTP dentro de uma nova thread com a classe AsyncTask.


Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *