Implementação de um Carregador de Imagens Remotas no Android

No desenvolvimento Android, uma tarefa comum é buscar recursos visuais de servidores externos. Para realizar essa operação de forma eficiente, é fundamental compreender a separação entre a thread principal (UI Thread) e threads secundárias, além de utilizar classes de conexão de rede e manipulação de mapas de bits.

Arquitetura da Solução

Para evitar que a interface do usuário trave durante o download de uma imagem (NetworkOnMainThreadException), a lógica deve ser dividida em duas partes:

  • Thread de Background: Responsável por estabelecer a conexão HTTP, baixar o fluxo de dados e convertê-lo em um objeto Bitmap.
  • Handler: Atua como ponte para enviar o objeto processado de volta para a thread principal, permitindo a atualização segura da ImageView.

Implementação do Código Principal

Abaixo está a implementação de uma Activity que captura uma URL de um campo de texto, processa o download e exibe o resultado.


package com.exemplo.imagefetcher;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class ImageLoaderActivity extends Activity {

    private EditText campoUrl;
    private ImageView containerImagem;
    private static final int SUCESSO_DOWNLOAD = 1;

    // Gerenciador de mensagens para atualizar a UI a partir de threads secundárias
    private final Handler comunicadorThread = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == SUCESSO_DOWNLOAD) {
                Bitmap imagemBaixada = (Bitmap) msg.obj;
                containerImagem.setImageBitmap(imagemBaixada);
            }
        }
    };

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

        campoUrl = findViewById(R.id.et_url_imagem);
        containerImagem = findViewById(R.id.iv_display_resultado);
        Button btnCarregar = findViewById(R.id.btn_executar_load);

        btnCarregar.setOnClickListener(v -> {
            String caminhoUrl = campoUrl.getText().toString().trim();
            if (TextUtils.isEmpty(caminhoUrl)) {
                Toast.makeText(ImageLoaderActivity.this, "Insira uma URL válida", Toast.LENGTH_SHORT).show();
                return;
            }
            iniciarDownload(caminhoUrl);
        });
    }

    private void iniciarDownload(final String endereco) {
        new Thread(() -> {
            try {
                URL urlDestino = new URL(endereco);
                HttpURLConnection conexao = (HttpURLConnection) urlDestino.openConnection();
                
                conexao.setRequestMethod("GET");
                conexao.setConnectTimeout(7000);
                conexao.setReadTimeout(7000);

                if (conexao.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    InputStream input = conexao.getInputStream();
                    
                    // Converte o fluxo de entrada diretamente em um Bitmap
                    Bitmap bitmapResultante = BitmapFactory.decodeStream(input);
                    
                    // Encapsula o bitmap em uma mensagem para o Handler
                    Message mensagem = Message.obtain();
                    mensagem.what = SUCESSO_DOWNLOAD;
                    mensagem.obj = bitmapResultante;
                    comunicadorThread.sendMessage(mensagem);
                    
                    input.close();
                }
                conexao.disconnect();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }
}

Pontos Chave do Funcionamento

1. HttpURLConnection: É a classe nativa para realizar requisições HTTP. Configuramos o método como "GET" e definimos timeouts para evitar que a thread fique pendurada indefinidamente caso o servidor não responda.

2. BitmapFactory.decodeStream: Esta é uma ferramenta poderosa que decodifica um InputStream vindo da rede diretamente em um objeto Bitmap, que o Android consegue renderizar.

3. Message.obtain(): Em vez de criar um novo objeto Message toda vez, utilizamos obtain() para recuperar mensagens de um pool global, o que é mais eficiente em termos de gerenciamento de memória no Android.

4. Permissões de Rede: Para que este código funcione, é obrigatório declarar a permissão de acesso à internet no arquivo AndroidManifest.xml:


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

Ao lidar com URLs que utilizam o protocolo HTTP simples (em vez de HTTPS) em versões mais recentes do Android, também pode ser necessário configurra o atributo android:usesCleartextTraffic="true" no manifesto.

Tags: android HttpURLConnection Handler BitmapFactory java

Publicado em 7-13 19:08