Como você baixa e salva uma imagem de um determinado url no Android?
fonte
Como você baixa e salva uma imagem de um determinado url no Android?
última grande atualização: 31 de março de 2016
TL; DR aka pare de falar, apenas me dê o código !!
Pule para o final deste post, copie a
BasicImageDownloader
(versão javadoc aqui ) em seu projeto, implemente aOnImageLoaderListener
interface e pronto.Nota : embora
BasicImageDownloader
lide com possíveis erros e evite que seu aplicativo trave caso algo dê errado, ele não executará nenhum pós-processamento (por exemplo, downsizing) no downloadBitmaps
.
Como este post recebeu bastante atenção, decidi retrabalhá-lo completamente para evitar que as pessoas usem tecnologias obsoletas, práticas de programação ruins ou apenas façam coisas bobas - como procurar por "hacks" para rodar a rede no thread principal ou aceitar todos os certificados SSL.
Eu criei um projeto de demonstração chamado "Image Downloader" que demonstra como baixar (e salvar) uma imagem usando minha própria implementação de downloader, o Android integrado DownloadManager
, bem como algumas bibliotecas populares de código aberto. Você pode visualizar o código-fonte completo ou baixar o projeto no GitHub .
Observação : ainda não ajustei o gerenciamento de permissões para SDK 23+ (Marshmallow), portanto, o projeto está voltado para o SDK 22 (Lollipop).
Em minha conclusão no final deste post, compartilharei minha humilde opinião sobre o caso de uso adequado para cada forma particular de download de imagens que mencionei.
Vamos começar com uma implementação própria (você pode encontrar o código no final do post). Em primeiro lugar, este é um ImageDownloader básico e pronto. Tudo o que ele faz é se conectar ao url fornecido, ler os dados e tentar decodificá-los como um Bitmap
, disparando os OnImageLoaderListener
callbacks da interface quando apropriado. A vantagem desta abordagem - é simples e você tem uma visão geral clara do que está acontecendo. Uma boa maneira de fazer isso se tudo que você precisa é baixar / exibir e salvar algumas imagens, embora você não se importe em manter uma memória / cache de disco.
Nota: no caso de imagens grandes, pode ser necessário reduzi-las .
-
O Android DownloadManager é uma maneira de permitir que o sistema faça o download para você. Na verdade, é capaz de baixar qualquer tipo de arquivo, não apenas imagens. Você pode permitir que o download aconteça silenciosamente e de forma invisível para o usuário ou pode permitir que o usuário veja o download na área de notificação. Você também pode registrar um BroadcastReceiver
para ser notificado após a conclusão do download. A configuração é bastante simples, consulte o projeto vinculado para obter o código de amostra.
O uso de DownloadManager
geralmente não é uma boa ideia se você também deseja exibir a imagem, já que seria necessário ler e decodificar o arquivo salvo em vez de apenas definir o baixado Bitmap
em um ImageView
. O DownloadManager
também não fornece nenhuma API para que seu aplicativo acompanhe o andamento do download.
-
Agora, a introdução das grandes coisas - as bibliotecas. Eles podem fazer muito mais do que apenas baixar e exibir imagens, incluindo: criar e gerenciar a memória / cache de disco, redimensionar imagens, transformá-las e muito mais.
Vou começar com o Volley , uma biblioteca poderosa criada pelo Google e coberta pela documentação oficial. Apesar de ser uma biblioteca de rede de propósito geral não especializada em imagens, o Volley apresenta uma API bastante poderosa para gerenciar imagens.
Você precisará implementar uma classe Singleton para gerenciar as solicitações do Volley e pronto.
Você pode querer substituir o seu ImageView
pelo do Volley NetworkImageView
, então o download se torna basicamente de uma linha:
((NetworkImageView) findViewById(R.id.myNIV)).setImageUrl(url, MySingleton.getInstance(this).getImageLoader());
Se você precisa de mais controle, esta é a aparência de criar um ImageRequest
com o Volley:
ImageRequest imgRequest = new ImageRequest(url, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
//do stuff
}
}, 0, 0, ImageView.ScaleType.CENTER_CROP, Bitmap.Config.ARGB_8888,
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//do stuff
}
});
É importante mencionar que o Volley apresenta um excelente mecanismo de tratamento de erros, fornecendo a VolleyError
classe que ajuda a determinar a causa exata de um erro. Se o seu aplicativo faz muitas redes e gerencia imagens não é o seu objetivo principal, então o Volley é a escolha perfeita para você.
-
O Picasso da Square é uma biblioteca bem conhecida que fará todo o carregamento de imagens para você. Exibir uma imagem usando o Picasso é tão simples quanto:
Picasso.with(myContext)
.load(url)
.into(myImageView);
Por padrão, o Picasso gerencia o cache de disco / memória, então você não precisa se preocupar com isso. Para obter mais controle, você pode implementar a Target
interface e usá-la para carregar sua imagem - isso fornecerá retornos de chamada semelhantes ao exemplo do Volley. Verifique o projeto de demonstração para exemplos.
O Picasso também permite que você aplique transformações à imagem baixada e há até mesmo outras bibliotecas que estendem essa API. Também funciona muito bem em um RecyclerView
/ ListView
/ GridView
.
-
Universal Image Loader é outra biblioteca muito popular que serve ao propósito de gerenciamento de imagens. Ele usa seu próprio ImageLoader
que (uma vez inicializado) tem uma instância global que pode ser usada para baixar imagens em uma única linha de código:
ImageLoader.getInstance().displayImage(url, myImageView);
Se você deseja acompanhar o andamento do download ou acessar o baixado Bitmap
:
ImageLoader.getInstance().displayImage(url, myImageView, opts,
new ImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
//do stuff
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
//do stuff
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
//do stuff
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
//do stuff
}
}, new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUri, View view, int current, int total) {
//do stuff
}
});
O opts
argumento neste exemplo é um DisplayImageOptions
objeto. Consulte o projeto de demonstração para saber mais.
Semelhante ao Volley, o UIL fornece a FailReason
classe que permite verificar o que deu errado em caso de falha de download. Por padrão, o UIL mantém um cache de memória / disco se você não disser explicitamente para não fazer isso.
Nota : o autor mencionou que ele não está mais mantendo o projeto em 27 de novembro de 2015. Mas como existem muitos contribuidores, podemos esperar que o Universal Image Loader continue vivo.
-
O Fresco do Facebook é a biblioteca mais nova e (IMO) mais avançada que leva o gerenciamento de imagens a um novo nível: desde evitar Bitmaps
o heap java (antes do Lollipop) até o suporte a formatos animados e streaming progressivo de JPEG .
Para aprender mais sobre as ideias e técnicas por trás do Fresco, consulte este post .
O uso básico é bastante simples. Observe que você precisará ligar Fresco.initialize(Context);
apenas uma vez, de preferência na Application
aula. Inicializar o Fresco mais de uma vez pode levar a um comportamento imprevisível e erros de OOM.
Fresco usa Drawee
s para exibir imagens, você pode pensar nelas como ImageView
s:
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/drawee"
android:layout_width="match_parent"
android:layout_height="match_parent"
fresco:fadeDuration="500"
fresco:actualImageScaleType="centerCrop"
fresco:placeholderImage="@drawable/placeholder_grey"
fresco:failureImage="@drawable/error_orange"
fresco:placeholderImageScaleType="fitCenter"
fresco:failureImageScaleType="centerInside"
fresco:retryImageScaleType="centerCrop"
fresco:progressBarImageScaleType="centerInside"
fresco:progressBarAutoRotateInterval="1000"
fresco:roundAsCircle="false" />
Como você pode ver, muitas coisas (incluindo opções de transformação) já são definidas em XML, então tudo que você precisa fazer para exibir uma imagem é uma linha:
mDrawee.setImageURI(Uri.parse(url));
Fresco fornece uma API de personalização estendida, que, sob circunstâncias, pode ser bastante complexa e requer que o usuário leia os documentos com atenção (sim, às vezes você precisa RTFM).
Incluí exemplos para JPEGs progressivos e imagens animadas no projeto de amostra.
Observe que o texto a seguir reflete minha opinião pessoal e não deve ser considerado um postulado.
Recycler-/Grid-/ListView
e não precisa de um monte de imagens para estar pronto para exibição, o BasicImageDownloader deve atender às suas necessidades.JSON
dados, trabalhe com imagens, mas esse não seja o objetivo principal do aplicativo, vá com o Volley .Caso você tenha perdido isso, o link do Github para o projeto de demonstração.
E aqui está o BasicImageDownloader.java
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Set;
public class BasicImageDownloader {
private OnImageLoaderListener mImageLoaderListener;
private Set<String> mUrlsInProgress = new HashSet<>();
private final String TAG = this.getClass().getSimpleName();
public BasicImageDownloader(@NonNull OnImageLoaderListener listener) {
this.mImageLoaderListener = listener;
}
public interface OnImageLoaderListener {
void onError(ImageError error);
void onProgressChange(int percent);
void onComplete(Bitmap result);
}
public void download(@NonNull final String imageUrl, final boolean displayProgress) {
if (mUrlsInProgress.contains(imageUrl)) {
Log.w(TAG, "a download for this url is already running, " +
"no further download will be started");
return;
}
new AsyncTask<Void, Integer, Bitmap>() {
private ImageError error;
@Override
protected void onPreExecute() {
mUrlsInProgress.add(imageUrl);
Log.d(TAG, "starting download");
}
@Override
protected void onCancelled() {
mUrlsInProgress.remove(imageUrl);
mImageLoaderListener.onError(error);
}
@Override
protected void onProgressUpdate(Integer... values) {
mImageLoaderListener.onProgressChange(values[0]);
}
@Override
protected Bitmap doInBackground(Void... params) {
Bitmap bitmap = null;
HttpURLConnection connection = null;
InputStream is = null;
ByteArrayOutputStream out = null;
try {
connection = (HttpURLConnection) new URL(imageUrl).openConnection();
if (displayProgress) {
connection.connect();
final int length = connection.getContentLength();
if (length <= 0) {
error = new ImageError("Invalid content length. The URL is probably not pointing to a file")
.setErrorCode(ImageError.ERROR_INVALID_FILE);
this.cancel(true);
}
is = new BufferedInputStream(connection.getInputStream(), 8192);
out = new ByteArrayOutputStream();
byte bytes[] = new byte[8192];
int count;
long read = 0;
while ((count = is.read(bytes)) != -1) {
read += count;
out.write(bytes, 0, count);
publishProgress((int) ((read * 100) / length));
}
bitmap = BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.size());
} else {
is = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
}
} catch (Throwable e) {
if (!this.isCancelled()) {
error = new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION);
this.cancel(true);
}
} finally {
try {
if (connection != null)
connection.disconnect();
if (out != null) {
out.flush();
out.close();
}
if (is != null)
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return bitmap;
}
@Override
protected void onPostExecute(Bitmap result) {
if (result == null) {
Log.e(TAG, "factory returned a null result");
mImageLoaderListener.onError(new ImageError("downloaded file could not be decoded as bitmap")
.setErrorCode(ImageError.ERROR_DECODE_FAILED));
} else {
Log.d(TAG, "download complete, " + result.getByteCount() +
" bytes transferred");
mImageLoaderListener.onComplete(result);
}
mUrlsInProgress.remove(imageUrl);
System.gc();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public interface OnBitmapSaveListener {
void onBitmapSaved();
void onBitmapSaveError(ImageError error);
}
public static void writeToDisk(@NonNull final File imageFile, @NonNull final Bitmap image,
@NonNull final OnBitmapSaveListener listener,
@NonNull final Bitmap.CompressFormat format, boolean shouldOverwrite) {
if (imageFile.isDirectory()) {
listener.onBitmapSaveError(new ImageError("the specified path points to a directory, " +
"should be a file").setErrorCode(ImageError.ERROR_IS_DIRECTORY));
return;
}
if (imageFile.exists()) {
if (!shouldOverwrite) {
listener.onBitmapSaveError(new ImageError("file already exists, " +
"write operation cancelled").setErrorCode(ImageError.ERROR_FILE_EXISTS));
return;
} else if (!imageFile.delete()) {
listener.onBitmapSaveError(new ImageError("could not delete existing file, " +
"most likely the write permission was denied")
.setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
return;
}
}
File parent = imageFile.getParentFile();
if (!parent.exists() && !parent.mkdirs()) {
listener.onBitmapSaveError(new ImageError("could not create parent directory")
.setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
return;
}
try {
if (!imageFile.createNewFile()) {
listener.onBitmapSaveError(new ImageError("could not create file")
.setErrorCode(ImageError.ERROR_PERMISSION_DENIED));
return;
}
} catch (IOException e) {
listener.onBitmapSaveError(new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION));
return;
}
new AsyncTask<Void, Void, Void>() {
private ImageError error;
@Override
protected Void doInBackground(Void... params) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(imageFile);
image.compress(format, 100, fos);
} catch (IOException e) {
error = new ImageError(e).setErrorCode(ImageError.ERROR_GENERAL_EXCEPTION);
this.cancel(true);
} finally {
if (fos != null) {
try {
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
protected void onCancelled() {
listener.onBitmapSaveError(error);
}
@Override
protected void onPostExecute(Void result) {
listener.onBitmapSaved();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static Bitmap readFromDisk(@NonNull File imageFile) {
if (!imageFile.exists() || imageFile.isDirectory()) return null;
return BitmapFactory.decodeFile(imageFile.getAbsolutePath());
}
public interface OnImageReadListener {
void onImageRead(Bitmap bitmap);
void onReadFailed();
}
public static void readFromDiskAsync(@NonNull File imageFile, @NonNull final OnImageReadListener listener) {
new AsyncTask<String, Void, Bitmap>() {
@Override
protected Bitmap doInBackground(String... params) {
return BitmapFactory.decodeFile(params[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null)
listener.onImageRead(bitmap);
else
listener.onReadFailed();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, imageFile.getAbsolutePath());
}
public static final class ImageError extends Throwable {
private int errorCode;
public static final int ERROR_GENERAL_EXCEPTION = -1;
public static final int ERROR_INVALID_FILE = 0;
public static final int ERROR_DECODE_FAILED = 1;
public static final int ERROR_FILE_EXISTS = 2;
public static final int ERROR_PERMISSION_DENIED = 3;
public static final int ERROR_IS_DIRECTORY = 4;
public ImageError(@NonNull String message) {
super(message);
}
public ImageError(@NonNull Throwable error) {
super(error.getMessage(), error.getCause());
this.setStackTrace(error.getStackTrace());
}
public ImageError setErrorCode(int code) {
this.errorCode = code;
return this;
}
public int getErrorCode() {
return errorCode;
}
}
}
Cursor
(noonActivityResult()
método) e, em seguida, criar umBitmap
usando esse caminho. E sim, você não deixará de usar umFileOutputStream
e umByteArrayOutputStream
se quiser salvar esta imagem em SD.Acabei de resolver este problema em e gostaria de compartilhar o código completo que posso baixar, salvar no sdcard (e ocultar o nome do arquivo) e recuperar as imagens e por fim verificar se a imagem já está lá. O url vem do banco de dados, portanto, o nome do arquivo pode ser facilmente usado com o id.
primeiro download de imagens
Em seguida, crie uma classe para salvar e recuperar os arquivos
Então, para acessar as imagens primeiro verifique se já está lá, senão faça o download
fonte
AsyncTask
vantagens do (uso adequado da parametrização, execução paralela ...). Por favor, consulte meus exemplos abaixo para obter detalhes. PS . para aqueles que podem pensar que escrevi isso para promover meu próprio código por qualquer motivo: não, estou apenas apontando os problemas que posso ver no exemplo fornecido.download
método, particularmente odoInBackground
método da tarefa que eu uso. UmIOException
pousaria nocatch (Throwable e)
bloco, resultando em umImageError
retorno e oonError()
retorno de chamada acionado. OImageError
objeto conterá o rastreamento de pilha original e a causa do ocorridoException
Por que você realmente precisa de seu próprio código para baixá-lo? Que tal passar seu URI para o gerenciador de download?
fonte
pode te ajudar ..
fonte
Tenho uma solução simples que está funcionando perfeitamente. O código não é meu, encontrei neste link . Aqui estão as etapas a seguir:
1. Antes de baixar a imagem, vamos escrever um método para salvar bitmap em um arquivo de imagem no armazenamento interno do Android. Ele precisa de um contexto, melhor usar a passagem no contexto do aplicativo por getApplicationContext (). Este método pode ser despejado em sua classe Activity ou outras classes util.
2. Agora que temos um método para salvar bitmap em um arquivo de imagem no andorid, vamos escrever a AsyncTask para baixar imagens por url. Esta classe privada precisa ser colocada em sua classe Activity como uma subclasse. Depois de fazer o download da imagem, no método onPostExecute, ele chama o método saveImage definido acima para salvar a imagem. Observe que o nome da imagem é codificado como “my_image.png”.
3. O AsyncTask para baixar a imagem está definido, mas precisamos executá-lo para executar essa AsyncTask. Para fazer isso, escreva esta linha em seu método onCreate em sua classe Activity, ou em um método onClick de um botão ou outros lugares que você achar necessário.
A imagem deve ser salva em /data/data/your.app.packagename/files/my_image.jpeg, verifique esta postagem para acessar este diretório a partir do seu dispositivo.
IMO isso resolve o problema! Se desejar outras etapas, como carregar a imagem, você pode seguir estas etapas extras:
4. Após o download da imagem, precisamos encontrar uma maneira de carregar o bitmap da imagem do armazenamento interno para que possamos usá-lo. Vamos escrever o método para carregar o bitmap da imagem. Este método leva dois parâmetros, um contexto e um nome de arquivo de imagem, sem o caminho completo, o context.openFileInput (imageName) irá procurar o arquivo no diretório de salvamento quando este nome de arquivo foi salvo no método saveImage acima.
5. Agora temos tudo o que precisamos para definir a imagem de um ImageView ou de quaisquer outras visualizações em que você gostaria de usar a imagem. Quando salvamos a imagem, codificamos o nome da imagem como “my_image.jpeg”, agora podemos passar esse nome de imagem para o método loadImageBitmap acima para obter o bitmap e defini-lo como ImageView.
6. Para obter o caminho completo da imagem pelo nome da imagem.
7. Verifique se o arquivo de imagem existe.
Arquivo arquivo =
Para excluir o arquivo de imagem.
Arquivo file = getApplicationContext (). GetFileStreamPath ("my_image.jpeg"); if (file.delete ()) Log.d ("arquivo", "minha_imagem.jpeg excluída!");
fonte
este código roda perfeitamente no meu projeto
fonte
Tente isto
fonte
RESULTADO
Certifique-se de adicionar permissão para gravar dados na memória
fonte
Como o Google diz, por enquanto, não se esqueça de adicionar também legível no armazenamento externo no manifesto:
Fonte: http://developer.android.com/training/basics/data-storage/files.html#GetWritePermission
fonte