Loading Image Asynchronously in Android ListView

* Last reviewed on: Apr 29, 2015

1. Introduction

As mobile devices are limited with memory, we must follow certain best practices to provide best performance and smooth user experience. Among set of best practices, the one holds priority is to take the long running heavy operations off the main thread.

Any long running tasks or heavy operations are usually performed in a different thread, to make sure your main thread does the minimum amount of work. Example of a typical long running tasks could be network operations, reading files form memory, animations, etc.

In this tutorial, we will create a simple ListView in Android that downloads data  asynchronously from the internet using a AsyncTask. As you can see in the screenshot below, the ListView contains a image thumbnails on each row, we will download the images asynchronously form server.

If you’re looking for downloading data from asynchronously from server, we recommend you to read through Android networking tutorial.

Async ListView Android Example

2. What is Android AsyncTask

AsyncTask enables you to implement MultiThreading without getting your hands dirty into threads. AsyncTask is easy to use, and it allows performing background operation in dedicated thread and passing the results back UI thread. If you are doing something isolated related to UI, for example downloading data for List view, go ahead and use AsyncTask. Some of the basic characteristics of AsyncTask are as follows

  1. An asynchronous task is defined by 3 generic types, called ParamsProgress and Result, and 4 steps, called onPreExecutedoInBackgroundonProgressUpdate and onPostExecute.
  2. In onPreExecute you can define code, which need to be executed before background processing starts.
  3. The doInBackground() method contains the code which needs to be executed in background, here in doInBackground we can send results to multiple times to event thread by publishProgress() method, to notify background processing has been completed we can return results simply.
  4. The onProgressUpdate() method receives progress updates from doInBackground method, which is published via publishProgress method, and this method can use this progress update to update event thread
  5. The onPostExecute() method handles results returned by doInBackground method.
  6. If an async task not using any types, then it can be marked as Void type.
  7. An running async task can be cancelled by calling cancel() method.

The generic types used by AsyncTask are

  • Params, the type of the parameters sent to the task upon execution
  • Progress, the type of the progress units published during the background computation.
  • Result, the type of the result of the background computation.

3. Downloading image using AsyncTask

We had learnt the basics of AsyncTask. Let us take a glance at how to use it practically for downloading image asynchronously from web. To achieve this, let us create a new class and name it as ImageDownloaderTask.

The following code snippet expects the url of image as an parameter and initiate download image download request. Once download is over, it displays the bitmap on the image view.

class ImageDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    private final WeakReference<ImageView> imageViewReference;

    public ImageDownloaderTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        return downloadBitmap(params[0]);
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                if (bitmap != null) {
                    imageView.setImageBitmap(bitmap);
                } else {
                    Drawable placeholder = imageView.getContext().getResources().getDrawable(R.drawable.placeholder);
                    imageView.setImageDrawable(placeholder);
                }
            }
        }
    }
}

4. Downloading image from web

Notice that, in the above step we are calling downloadBitmap() method but haven’t declared it yet. Let us create declare the  downloadBitmap method which takes care of loading image and returning the bitmap. Here we are using HttpURLConnection to download the stream from given url. Learn more about HttpURLConnection from our android networking tutorial.

private Bitmap downloadBitmap(String url) {
    HttpURLConnection urlConnection = null;
    try {
        URL uri = new URL(url);
        urlConnection = (HttpURLConnection) uri.openConnection();
        int statusCode = urlConnection.getResponseCode();
        if (statusCode != HttpStatus.SC_OK) {
            return null;
        }

        InputStream inputStream = urlConnection.getInputStream();
        if (inputStream != null) {
            Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
            return bitmap;
        }
    } catch (Exception e) {
        urlConnection.disconnect();
        Log.w("ImageDownloader", "Error downloading image from " + url);
    } finally {
        if (urlConnection != null) {
            urlConnection.disconnect();
        }
    }
    return null;
}

5. Creating custom ListView

Now that we understand the basics of AsyncTask, let us proceed with creating the custom list view in android. The focus of this tutorial is tried to image download. If you not familiar with creating custom list view in android, you can read our Android ListView tutorial.

5.1. Adding ListView to activity layout

For sake of simplicity our activity layout contains a simple ListView that covers the total available width and height of the device. Create a new file activity_main.xml in layout directory.

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"   
        android:id="@+id/custom_list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
</ListView>

5.2. Create list view activity

Let us now create a new activity class MainActivity.java in your project src directory and paste the following code. We will complete this activity in Section 3.5

public class MainActivity extends Activity {

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

5.3. Create list row layout

Now let us focus on the layout for list view row item. As you can notice form the above screenshot, we will use RelativeLayout for building a simple list row view. Crete a new file list_row_layout.xml file in layout directory and paste the following code blocks.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:minHeight="50dp"
    android:padding="8dp">

    <ImageView
        android:id="@+id/thumbImage"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dp"
        android:background="@drawable/placeholder" />

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/thumbImage"
        android:minLines="2"
        android:paddingTop="5dp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/reporter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/title"
        android:layout_marginTop="5dp"
        android:layout_toRightOf="@id/thumbImage" />

    <TextView
        android:id="@+id/date"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/reporter"
        android:layout_alignParentRight="true" />

</RelativeLayout>

5.4. Creating custom list adapter

Adapter is acts as a bridge between data source and adapter views such as ListView, GridView. Adapter iterates through the data set from beginning till the end and generate Views for each item in the list.

Create a new class named CustomListAdapter and extend it from BaseAdapter. Visit here to learn more about android adapters.

public class CustomListAdapter extends BaseAdapter {
	private ArrayList listData;
	private LayoutInflater layoutInflater;

	public CustomListAdapter(Context context, ArrayList listData) {
		this.listData = listData;
		layoutInflater = LayoutInflater.from(context);
	}

	@Override
	public int getCount() {
		return listData.size();
	}

	@Override
	public Object getItem(int position) {
		return listData.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder;
		if (convertView == null) {
			convertView = layoutInflater.inflate(R.layout.list_row_layout, null);
			holder = new ViewHolder();
			holder.headlineView = (TextView) convertView.findViewById(R.id.title);
			holder.reporterNameView = (TextView) convertView.findViewById(R.id.reporter);
			holder.reportedDateView = (TextView) convertView.findViewById(R.id.date);
			holder.imageView = (ImageView) convertView.findViewById(R.id.thumbImage);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}

		NewsItem newsItem = (NewsItem) listData.get(position);
		holder.headlineView.setText(newsItem.getHeadline());
		holder.reporterNameView.setText("By, " + newsItem.getReporterName());
		holder.reportedDateView.setText(newsItem.getDate());
		if (holder.imageView != null) {
			new ImageDownloaderTask(holder.imageView).execute(newsItem.getUrl());
		}
		return convertView;
	}

	static class ViewHolder {
		TextView headlineView;
		TextView reporterNameView;
		TextView reportedDateView;
		ImageView imageView;
	}
}

5.5. Using list view adapter

Now that we have the list view and adapter class ready. Let us proceed to complete the MainActivity class. The following code snippet is used to initialize the list view and assign the custom adapter to it.

public class MainActivity extends Activity {

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

        ArrayList<ListItem> listData = getListData();

        final ListView listView = (ListView) findViewById(R.id.custom_list);
        listView.setAdapter(new CustomListAdapter(this, listData));
        listView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> a, View v, int position, long id) {
                ListItem newsData = (ListItem) listView.getItemAtPosition(position);
                Toast.makeText(MainActivity.this, "Selected :" + " " + newsData, Toast.LENGTH_LONG).show();
            }
        });
    }

    private ArrayList<ListItem> getListData() {
        ArrayList<ListItem> listMockData = new ArrayList<ListItem>();
        String[] images = getResources().getStringArray(R.array.images_array);
        String[] headlines = getResources().getStringArray(R.array.headline_array);

        for (int i = 0; i < images.length; i++) {
            ListItem newsData = new ListItem();
            newsData.setUrl(images[i]);
            newsData.setHeadline(headlines[i]);
            newsData.setReporterName("Pankaj Gupta");
            newsData.setDate("May 26, 2013, 13:35");
            listMockData.add(newsData);
        }
        return listMockData;
    }
}

Notice that, getListData() method in the activity is used to create some dummy list data for the list view. To make this example simple, we are using the string arrays defined in the strings.xml resource file. But in realtime you might download the data from server or get it from any other sources.

Add the following string array declarations to string.xml file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Async ListView</string>
    <array name="images_array">
        <item>http://lh5.ggpht.com/_hepKlJWopDg/TB-_WXikaYI/AAAAAAAAElI/715k4NvBM4w/s144-c/IMG_0075.JPG</item>
        <item>http://lh4.ggpht.com/_4f1e_yo-zMQ/TCe5h9yN-TI/AAAAAAAAXqs/8X2fIjtKjmw/s144-c/IMG_1786.JPG</item>
        <item>http://lh3.ggpht.com/_GEnSvSHk4iE/TDSfmyCfn0I/AAAAAAAAF8Y/cqmhEoxbwys/s144-c/_MG_3675.jpg</item>
        <item>http://lh6.ggpht.com/_ZN5zQnkI67I/TCFFZaJHDnI/AAAAAAAABVk/YoUbDQHJRdo/s144-c/P9250508.JPG</item>
        <item>http://lh4.ggpht.com/_XjNwVI0kmW8/TCOwNtzGheI/AAAAAAAAC84/SxFJhG7Scgo/s144-c/0014.jpg</item>
        <item>http://lh6.ggpht.com/_Nsxc889y6hY/TBp7jfx-cgI/AAAAAAAAHAg/Rr7jX44r2Gc/s144-c/IMGP9775a.jpg</item>
        <item>http://lh6.ggpht.com/_ZN5zQnkI67I/TCFFZaJHDnI/AAAAAAAABVk/YoUbDQHJRdo/s144-c/P9250508.JPG</item>
    </array>

    <array name="headline_array">
        <item>Dance of Democracy</item>
        <item>Major Naxal attacks in the past</item>
        <item>BCCI suspends Gurunath pending inquiry </item>
        <item>Life convict can`t claim freedom after 14 yrs: SC</item>
        <item>Indian Army refuses to share info on soldiers mutilated at LoC</item>
        <item>French soldier stabbed; link to Woolwich attack being probed</item>
        <item>Life convict can`t claim freedom after 14 yrs: SC</item>
    </array>
</resources>

5. Download Complete Example

Download from GitHub.

A blogger, speaker, author, a bit of tech freak and a software developer. He is a thought leader in the fusion of design and mobile technologies. He has over 8 years of experience in developing rich mobile applications in Android, HTML5, Xamarin and PhoneGap.

  • NishaRathi

    Geeting NULL Pointer Exception in downloadBitmap()
    urlConnection.disconnect();

  • Hms

    why download link not working?

    • Nilanchala Panigrahy

      Have fixed the download url.

  • Nilanchala Panigrahy

    Updated the complete example. Have a check.

    • Rana Saqib Manj

      Sir !! I am waiting for your reply….

      • Nilanchala Panigrahy

        Your parser is not fetching image. It gets null value for image url.

        Also, update ImageDownloaderTask with following to avoid exception

        if (urlConnection != null){
        urlConnection.disconnect();
        }

        • Rana Saqib Manj

          plz solve this issue……. and then send me code …. thanx

    • Rana Saqib Manj

      plz solve this issue.. I am very thankful to you …… please

      • Nilanchala Panigrahy

        done.

  • Rana Saqib Manj

    Hello Sir !!! I Have a question …… In class Adapter .. you use NewsItem … I don’t understand this NewsItem……which place you declared this class…… plz reply me asap….. thanx

  • 杰民 赵

    down code link failure , you can come here https://github.com/javatechig/Advance-Android-Tutorials

    • http://javatechig.com/ JavaTechig

      Yes it is currently taking to github link. The project is available on git.

  • peeyush singhal

    hello sir, i have a problem when i am running this application it shows only R.drawable.list_placeholde image. How can i solve this problem please suggest me??

  • bbb

    -1

  • ali AS

    Hi sir,Its a great tutuorial but link is broken.Could you provide a new one please

  • wallaceharris221@gmail.com

    hey plz give me the download link

  • Nagul

    this wont work correctly. Since you are using viewholder the views are recycled so if your asynctask is downloading an image and you scroll the list down, you wound end up with a wrong image at the wrong place.

    • Milan Maharjan

      yep, he’s right

  • Gabriel Peixoto

    I changed the size of images to 300dp. So, I found a bug. The List showed repeated images and in wrong position.

  • Kevin

    Hello, thanks for this code, but unfortunately I have a problem. GetView method is not called although my size is not null. Can you help me?
    Regards,
    K.L

  • Michael

    Hey, Could you give a link to github ?? I’ve liked this article but the content is still locked :/ It ll be very helpful to me.
    regards,
    Michael

  • Nilanchala Panigrahy

    Can you clear with your question. I cant guess what you asking..

  • avo28

    hello, thanks for this tutorial I worked perfect, but I have a problem, I’m using Spanish text, and accepted and the letter “Ñ” default character out with strangers, because you use clear text. java and do not know how to bring it from string. xml, I could tell how to fix this .. am newbie please calmly .. Note: I’m sorry for bad translation yet translating

  • avo28

    hola, gracias por este tutorial me funciono perfecto, pero tengo un problema, estoy utilizando texto en español, y sus aceptos y la letra “Ñ” sale con carcateres extraños, obvio porque usas texto en .java y no se como traerlo desde string.xml, me podrian decir como solucionar esto.. por favor soy novato con calma.. nota: estoy traduciendo perdon por mala traducion

  • Jyotika

    nice tutorial… its is easy to understand :)

  • Alejandro Casanova

    Thanks, excellent tutorial. The solution works just fine, but I have a problem when loading contact images, when I scroll the ListView the imageview of a row begins to change from the first contact image until de last one that was once in that position. I tried to initially put the default image to show but it only appears for a short time, then begins to change until the last one finishes loading.

    Here is my modification, but it doesn’t solves the problem completely.

    if (holder.image != null) {

    holder.image.setImageDrawable(getContext().getResources().

    getDrawable(R.drawable.ic_contact_unknown));

    new ImageDownloaderTask(holder.image).execute(contact);

    }

    How to avoid Imageview from reloading until the scrolling stops showing only a default image?

    Thanks.

  • http://www.facebook.com/art.match.5 Art Match

    Thank you for sharing. It helped me..

  • Akuma Manalo

    hi Sir,thanks for this tutorial,I would like to ask how to insert the current date automatically in listview? thanks

    • http://javatechig.com/ javatechig

      You can call getCurrentTime method from getView method.

      holder.reportedDateView.setText(getCurrentTime());


      public String getCurrentTime() {
      SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //dd/MM/yyyy
      Date now = new Date();
      String strDate = sdfDate.format(now);
      return strDate;
      }

      • Akuma Manalo

        thanks this works =)