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.