Descargar Imagen desde Url en Android

Buenos días, en esta entrada veremos como podemos bajar una imagen para mostrarla en nuestro Android, mediante una simple función.


Cuando creamos aplicaciones, podemos encontrarnos en la necesidad de tener que bajar imágenes y tener que mostrarlas luego en un ImageView, y a partir de la versión 3.X de Android, llamada Honeycomb, cualquier operación que requiera el uso de Internet debe ir en otro hilo aparte, no puede ir en el hilo principal de la aplicación, es por eso por lo que tendremos que usar un AsyncTask para usar la red de nuestro dispositivo.

  • GUI

    Una vez creado nuestro proyecto en Android, vamos a modificar el xml de la actividad, con lo que nos quedará algo así:

    			<RelativeLayout 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" >
    			
    			    <ImageView 
    			        android:id="@+id/imagen"
    			        android:layout_width="wrap_content"
    			        android:layout_height="wrap_content"
    			        android:layout_centerInParent="true"/>
    			
    			</RelativeLayout>
    			

    Con este xml tendremos un elemento ImageView que estará posicionado en el centro de nuestra pantalla.

  • Activity

    Aquí viene la parte importante de nuestro ejemplo, ya que toda la funcionalidad principal se desarrolla aquí.

    Vamos a empezar declarando 1 constante, la cual contendrá la url de una imagen, yo he cogido una del buscador de Google, podeis usar cualquiera que esté alojada en la red. A continuación, tenemos la variable de instancia del ImageView, en el cual pondremos la imagen descargada.

    			public class MainActivity extends Activity {
    	
    				public static final String URL = "http://www.thebiblescholar.com/android_awesome.jpg";
    				private ImageView imgImagen;
    			}
    			

    Método onCreate()

    En este método inicializaremos el ImageView, y llamaremos a la AsyncTask, la cual descargará la imagen y la establecerá en el ImageView.

    			@Override
    			protected void onCreate(Bundle savedInstanceState) {
    				super.onCreate(savedInstanceState);
    				setContentView(R.layout.activity_main);
    				
    				imgImagen = (ImageView)findViewById(R.id.imagen);
    				
    				CargaImagenes nuevaTarea = new CargaImagenes();
    				nuevaTarea.execute(URL);
    				
    			}
    			

    En este punto nuestra aplicación aún no será funcional, ya que no tenemos creada la AsyncTask, la cual yo he llamado CargarImagenes, pero cualquier otro nombre valdría.

    AsyncTask CargaImagenes

    Aquí viene una de las partes principales de nuestra aplicación, será la tarea que se encargue de bajar la imagen y establecerla en el ImageView, y todo esto se realiza en segundo plano mientras muestra un ProgressDialog, para que el usuario sepa que nuestra aplicación no ha explotado.

    			private class CargaImagenes extends AsyncTask<String, Void, Bitmap>{
    		
    				ProgressDialog pDialog;
    		
    				@Override
    				protected void onPreExecute() {
    					// TODO Auto-generated method stub
    					super.onPreExecute();
    					
    					pDialog = new ProgressDialog(MainActivity.this);
    					pDialog.setMessage("Cargando Imagen");
    					pDialog.setCancelable(true);
    					pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
    					pDialog.show();
    					
    				}
    		
    				@Override
    				protected Bitmap doInBackground(String... params) {
    					// TODO Auto-generated method stub
    					Log.i("doInBackground" , "Entra en doInBackground");
    					String url = params[0];
    					Bitmap imagen = descargarImagen(url);
    					return imagen;
    				}
    				
    				@Override
    				protected void onPostExecute(Bitmap result) {
    					// TODO Auto-generated method stub
    					super.onPostExecute(result);
    					
    					imgImagen.setImageBitmap(result);
    					pDialog.dismiss();
    				}
    				
    			}
    			

    Como vemos, la AsyncTask tiene un ProgressDialog, el cual es inicializado y lanzado en el método onPreExecute(), el cual es llamado automáticamente antes de empezar el método doInBackground().

    En el método doInBackground() se realiza la funcionalidad principal de la aplicación. Primero cogemos el parámetro de la lista de parámetros, en este caso es param[0], porque solo hemos pasado uno, y este se encuentra en la posición 0, como si de un Array se tratase.

    Luego definimos un objeto de la clase Bitmap, que será el encargado de guardar la imagen.

    • Método descargarImagen()

      Este método es simple, es el siguiente:

      					private Bitmap descargarImagen (String imageHttpAddress){
      						URL imageUrl = null;
      						Bitmap imagen = null;
      						try{
      							imageUrl = new URL(imageHttpAddress);
      							HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
      							conn.connect();
      							imagen = BitmapFactory.decodeStream(conn.getInputStream());
      						}catch(IOException ex){
      							ex.printStackTrace();
      						}
      						
      						return imagen;
      					}
      					

      En este método recibimos la url de la imagen a descargar, el cual pasaremos mas tarde como argumento para crear una nueva instancia de la clase URL.

    Por último, en el método onPostExecute(), el cual se inicia al terminar el método doInBackground(), y en este es donde podremos interactuar con el hilo principal de la aplicación, por tanto quitaremos el ProgressDialog, con lo cual el usuario verá que la tarea habrá terminado, y a continuación con el método setImageBitmap(), establecemos la imagen en el ImageView.

  • AndroidManifest.xml

    Nada funcionará en nuestra aplicación si no añadimos una linea en nuestro AndroidManifest.xml. Es simple, en nuestra aplicación estamos usando Internet para descargar una imagen, y es un permiso que tenemos que declarar. Para ello, nos iremos al directorio raiz de nuestro proyecto y abriremos el archivo AndroidManifest.xml.

    Pinchamos en la última pestaña que vemos en la parte inferior de la ventana, la cual se llama AndroidManifest.xml, y añadimos la linea correspondiente, con lo cual quedará así:

    			<?xml version="1.0" encoding="utf-8"?>
    			<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    			    package="sekth.droid.bajarimagen"
    			    android:versionCode="1"
    			    android:versionName="1.0" >
    			
    			    <uses-sdk
    			        android:minSdkVersion="8"
    			        android:targetSdkVersion="16" />
    			
    			    <application
    			        android:allowBackup="true"
    			        android:icon="@drawable/ic_launcher"
    			        android:label="@string/app_name"
    			        android:theme="@style/AppTheme" >
    			        <activity
    			            android:name="sekth.droid.bajarimagen.MainActivity"
    			            android:label="@string/app_name" >
    			            <intent-filter>
    			                <action android:name="android.intent.action.MAIN" />
    			
    			                <category android:name="android.intent.category.LAUNCHER" />
    			            </intent-filter>
    			        </activity>
    			    </application>
    			
    			</manifest>
    			

    La línea que hemos agregado es la siguiente:

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

    Con esta línea nuestra aplicación podrá usar internet para realizar acciones que tengan que ver con ello.

Con esto nuestra aplicación es finalmente funcional, y podemos ver que al abrirla se nos carga la imagen. A continuación dejo una foto y un video de como es en funcionamiento.

Aquí la foto

Aquí un video demostrativo


Sin más esto es todo, cualquier aporte o corrección es bienvenido, espero que os sirva.

Saludos!!

  • Pingback: Guardar Imagen en Memoria Interna Android « SekthDroid()

  • Gerson Aguirre

    Muy buen tutorial, solo me gustaria consultar, como hago que esto funcione con un boton?

    Saludos desde El Salvador

    • Buenas Gerson, como ves en el tutorial todo se realiza en el método onCreate, es decir cuando se está generando la Activity. Si quieres que se ejecute con un botón, bastaría con agregarlo en la UI, y añadirle un onClickListener, o bien mediante el atributo onClick del xml, y dentro de el instanciar la AsynkTask y ejecutarla. Debido a que no estoy en mi casa y no tengo internet no puedo ponerte un ejemplo ahora mismo, ya que estoy en el ejemplo, en cuanto vuelva te pongo el código de ejemplo con un botón por si sirve de ayuda. Saludos y siento ahora mismo no poder ponerte el ejemplo, pero desde el movil es complicado 🙂

  • Johann

    Hola, gran tuto… una pregunta.. adonde guarda la imagen que se descargo, en cache.. en la memoria interna o externa ? es q necesito crear llamar una serie de imagenes dinamicamente desde internet y son grandes para un movil, no se siguardarlas en cache o descargarlas primero en la sd y luego llamarlas en tiempo de ejecucion, y de ser asi como se hace esto??

  • Buenas Johann,

    En esta entrada lo único que se hace es descargar una imagen y colocarla en un ImageView, pero no la guarda ni en la memoria interna ni externa.

    Para guardarlo en la memoria externa, debes usar algo parecido a lo que aparece en Android Developer. Lo primero comprobar si está disponible y se puede escribir en ella:

    /* Checks if external storage is available for read and write */
    public boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        }
        return false;
    }
    
    /* Checks if external storage is available to at least read */
    public boolean isExternalStorageReadable() {
        String state = Environment.getExternalStorageState();
        if (Environment.MEDIA_MOUNTED.equals(state) ||
            Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
            return true;
        }
        return false;
    }
    

    Una vez tengas los datos necesarios, puedes hacer uso de estos métodos:

    Descargar Imagen

    private Bitmap downloadImage(String imageHttpAddress){
    		URL imageUrl = null;
    		Bitmap loadedImage = null;
    		try{
    			
    			imageUrl = new URL(imageHttpAddress);
    			HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
    			conn.connect();
    			loadedImage = BitmapFactory.decodeStream(conn.getInputStream());
    		}catch(IOException ex){
    			ex.printStackTrace();
    		}
    		
    		return loadedImage;
    	}
    

    Guardar Imagen en SD

    private void saveImageExternalStorage(Bitmap imagen){
        	if ((mExternalStorageAvailable == true) && (mExternalStorageWritable == true)){
        		String root = Environment.getExternalStorageDirectory().toString();
        		File cfImagenes = new File(root + "/NombreDeLaCarpeta/");
        		if (cfImagenes.isDirectory() == false){
        			cfImagenes.mkdir();
        		}else{
            		String imgNombre = "NombreDeLaImagen" + ".jpg";
            		File file = new File(cfImagenes, imgNombre);
            		if (file.exists() == false){
            			try{
            				FileOutputStream out = new FileOutputStream(file);
            				imagen.compress(Bitmap.CompressFormat.JPEG, 100, out);
            				out.flush();
            				out.close();
            			}catch (IOException ex){
            				ex.printStackTrace();
            			}
            		}
            	}
        	}
        	
        }
    

    Estos métodos son los que he usado varias veces, pero al fin y al cabo el tema de bajar imágenes va en función de tu proyecto, de lo que quieras hacer, del comportamiento y el enfoque que le des, ya que si son tamaños de imágenes grandes puede que te convenga descargarlos si la conexión que existe es WiFi únicamente, para no consumir demasiados datos en caso contrario.

    Igualmente en el blog de Android que te he puesto arriba aparece algo de hacer Caché con imágenes.

    Espero que te sirva de ayuda!

    Saludos!!

  • Milton Paredes

    Hey amigo por favor he visto tutoriales en toda la web y el procedimiento es el mismo pero no me corre la aplicación me sale que la aplicacion ha sido detenida

    • Buenas Milton,

      Has consultado el Log para ver que tipo de excepción te lanza?

      Quizás se te pueda haber olvidado darle permisos para usar internet a la aplicación mediante el AndroidManifest.xml

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

      Saludos, y cualquier cosa no dudes en preguntar.

      • Milton Paredes

        SI ESTA PUESTO EL PERMISO EN EL MANIFIESTO

      • Milton Paredes

        Muchas gracias por la respuesta espero me lo puedas facilitar

    • Milton Paredes

      me podrias pasar tu programa a mi correo por favor seria de gran ayuda

  • Kivpson

    Excelente tutorial, te agradezco un montón, por otra parte si quisiera que esta imagen se almacene en un directorio especifico y que la app lo tome desde ahí después, como harías?

    • Buenas Kivpson,

      Si quisieras guardar una imagen en algún directorio especial, puedes crear un directorio en la memoria interna usando la clase ContextWrapper, de la siguiente manera:

      public class MainActivity extends Activity {
      	public static final String DIRECTORIO_IMAGENES = "imagenes";
      	
      	@Override
      	protected void onCreate(Bundle savedInstanceState) {
      		super.onCreate(savedInstanceState);
      		setContentView(R.layout.activity_main);
      		
      		ContextWrapper cw = new ContextWrapper(this);
      		
      		// Obtenemos la ruta al directorio con el nombre que hemos puesto como primer
      		// argumento, y si no existe lo crea
      		File dir = cw.getDir(DIRECTORIO_IMAGENES, Context.MODE_PRIVATE);
      		
      		// Obtenemos una lista con los archivos que existen dentro e iteramos para realizar
      		// lo que queramos
      		File[] archivos = dir.listFiles();
      		for (File imagen : archivos){
      			Log.i("Archivo encontrado", imagen.getAbsolutePath());
      			Log.i("Archivo encontrado", imagen.getName());
      		}
      	}
      
      }
      

      De esta manera, puedes obtener el directorio y trabajar con el como quieras.

      Por otro lado, si lo que quieres es guardar la ruta para acceder a ella directamente, deberías guardar la ruta de los archivos bien en las preferencias o mediante una base de datos.

      Espero que te sea de ayuda!

      Saludos!!

  • Vacan excelente tu tutorial, soy muy novato en esto pero me gustaria saber como cargar varias imagenes creando asi varios imagesviews?

    gracias

    • Buenas Freddy

      Lo primero de todo me alegro de que te haya sido de utilidad y gracias por comentar.

      Por otro lado, si lo que quieres es mostrar varios “Imageviews” como si fuera una parrilla de imágenes, la mejor implementación seria usar un “GridView”, los cuales se pueden llenar con una clase que herede de “BaseAdapter” para cargar cada elemento con una imagen.

      Tenia pensado hace tiempo sacar una entrada enseñando esto mismo, pero por unas cosas u otras no he podido. Intentare hacer uno este fin de semana sobre este tipo de implementación y si aun sigues con la duda te puede ayudar.

      De igual modo, busca mas información sobre esto que te comento porque creo que es lo que estas buscando.

      Espero que te sea de ayuda.

      Saludos!!

  • Yo solo quiero consultar porque si descarga los imagenes como son varias tiende a desordenarse las imganes. pero la informacion sigue igual es por los hilos.

    • Buenas Ronald,

      ¿Cual es realmente tu problema?

      Saludos!

    • Ya lo solucione es un problema de Hilos y un efecto de ListView.

  • Disculpa ya has usado websockets desde android tienes alguna libreria. Estoy intentando conectarme con esta libreria Java-WebSocket a mi servidor el archivo es .ashx con javascript me conecto correctamente. desde android se cierra lo conexion inmediatamente se crea el objeto. Gracias.

    • Buenas Ronald

      Personalmente no he tenido oportunidad de usar sockets entre Android y un servidor, por lo que me temo que no puedo ayudarte con este tema, lo siento.

      Saludos, y espero que lo soluciones!