Android: Asynchronously Load Image from SD card
Trying to load images from the SD card into my listview within the UI thread (directly in bindView())made scrolling the listview very jerky and slow – it worked but was barely acceptable. My initial attempt at asynchronously loading the images from the SD card ran into a bit of a problem when I discovered that the Views for each row on a list are reused when they go off screen as initialising the Views is a costly process. This meant with the way I’d written my code that quickly scrolling the list would cause the ImageView to flicker between all the other images I’d loaded into it before settling on the final one. This is because all the AsyncTasks that’d been created were yet to finish and calling onPostExecute() binding the view before a new one was created!
So I looked into how to solve this problem. The code/tutorial in the post, Multithreading For Performance on the Android Developer blog, asynchronously downloads/binds an image from the Internet. I’ve adapted this to load the image from the SD card. Here’s my class, SDImageLoader:
package com.samcoles.specimenhunter.ui;
import java.lang.ref.WeakReference;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.widget.ImageView;
public class SDImageLoader {
public void load(String filePath, ImageView v) {
if(cancelPotentialSDLoad(filePath, v)) {
SDLoadImageTask task = new SDLoadImageTask(v);
SDLoadDrawable sdDrawable = new SDLoadDrawable(task);
v.setImageDrawable(sdDrawable);
task.execute(filePath);
}
}
private Bitmap loadImageFromSDCard(String filePath) {
BitmapFactory.Options bfo = new BitmapFactory.Options();
bfo.inSampleSize = 4;
bfo.outWidth = 150;
bfo.outHeight = 150;
Bitmap photo = BitmapFactory.decodeFile(filePath, bfo);
return photo;
}
private static boolean cancelPotentialSDLoad(String filePath, ImageView v) {
SDLoadImageTask sdLoadTask = getAsyncSDLoadImageTask(v);
if(sdLoadTask != null) {
String path = sdLoadTask.getFilePath();
if((path == null) || (!path.equals(filePath))) {
sdLoadTask.cancel(true);
} else {
return false;
}
}
return true;
}
private static SDLoadImageTask getAsyncSDLoadImageTask(ImageView v) {
if(v != null) {
Drawable drawable = v.getDrawable();
if(drawable instanceof SDLoadDrawable) {
SDLoadDrawable asyncLoadedDrawable = (SDLoadDrawable)drawable;
return asyncLoadedDrawable.getAsyncSDLoadTask();
}
}
return null;
}
private class SDLoadImageTask extends AsyncTask<String, Void, Bitmap> {
private String mFilePath;
private final WeakReference<ImageView> mImageViewReference;
public String getFilePath() {
return mFilePath;
}
public SDLoadImageTask(ImageView v) {
mImageViewReference = new WeakReference<ImageView>(v);
}
@Override
protected void onPostExecute(Bitmap bmp) {
if(mImageViewReference != null) {
ImageView v = mImageViewReference.get();
SDLoadImageTask sdLoadTask = getAsyncSDLoadImageTask(v);
// Change bitmap only if this process is still associated with it
if(this == sdLoadTask) {
v.setImageBitmap(bmp);
}
}
}
@Override
protected Bitmap doInBackground(String... params) {
mFilePath = params[0];
return loadImageFromSDCard(mFilePath);
}
}
private class SDLoadDrawable extends ColorDrawable {
private final WeakReference<SDLoadImageTask> asyncSDLoadTaskReference;
public SDLoadDrawable(SDLoadImageTask asyncSDLoadTask) {
super(Color.BLACK);
asyncSDLoadTaskReference = new WeakReference<SDLoadImageTask>(asyncSDLoadTask);
}
public SDLoadImageTask getAsyncSDLoadTask() {
return asyncSDLoadTaskReference.get();
}
}
}
To use this, within your list adapter create an SDImageLoader as a member:
private final SDImageLoader mImageLoader = new SDImageLoader();
and within your overridden bindView() method simply call the method load, passing it a string representing the absolute filepath and the ImageView you want it to be loaded into. e.g.
mImageLoader.load(photoFilePath, photoView);
Here’s that in action:
A working example project based on this post can be found on github.
This entry was posted by Sam Coles on May 22, 2011 at 3:18 pm, and is filed under Mobile. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site.

Great article, I was looking for a way to Asynchronously Load Images and avoid OutOfMemory and you example works very well…thanks for sharing!
Can you give a further example on how to use the sdImageLoader in an listadapter. Im trying to use your example and Im really stuck :p Im also not sure how to use an instance of it in order to get it to pull images that I can populate a listview with
Any help would be greatly appreciated
[...] the code for a simple full screen ImageViewerActivity. It uses the SDImageLoader from this post to load the image asynchronously from the SD [...]
the code is perfect ,I love this code style. thanks for shareing us you code.
Thank you very much man
Improve your google rank today check this out! http://goo.gl/YzNKa