August 11, 2009

Android ImageLoader - load images sequencially in the background

"A few days ago I started to learn android… and it’s been a fairly smooth transition from flash. Although I have to say, as flash developers we’re just spoiled. We take for granted all the background stuff flash does for us to make coding that much easier.

One of those things is Loading images. in flash we have the Loader class which makes loading images very easy.

var loader:Loader = new Loader();

loader.load(new URLRequest("myimage.jpg"));

In java/android it takes a few more lines

HttpURLConnection conn = (HttpURLConnection) new URL("myimage.jpg").openConnection();

conn.setDoInput(true);

conn.connect();

InputStream inStream = conn.getInputStream();

Bitmap bitmap = BitmapFactory.decodeStream(inStream);

inStream.close();

conn.disconnect();

The code above runs in the same thread so you’ll lock up your UI until the image has finish loading. Adding the Threading code adds quite a bit more code. So I decided to create an ImageLoader class to make my life just a little easier. Now I can load an image to an ImageView with one line of code.

ImageLoader.getInstance().load(myImageView, "myimage.jpg", true);

The last parameter tells the ImageLoader class to cache that the bitmap.

The ImageLoader class loads images sequentially so you don’t slow your mobile device down to a crawl. To cancel Loading simply call clearQueue() and to clear the cache call clearCache()

Here’s the class

package com.wumedia.net;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.HashMap;

import java.util.Iterator;

import java.util.LinkedList;

import java.util.Queue;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.os.Handler;

import android.widget.ImageView;

public class ImageLoader {

static private ImageLoader _instance;

static public ImageLoader getInstance() {

if (_instance == null) {

_instance = new ImageLoader();

}

return _instance;

}

private HashMap _urlToBitmap;

private Queue _queue;

private DownloadThread _thread;

private Bitmap _missing;

private boolean _busy;

/**

* Constructor

*/

private ImageLoader () {

_urlToBitmap = new HashMap();

_queue = new LinkedList();

_busy = false;

}

public Bitmap get(String url) {

return _urlToBitmap.get(url);

}

public void load(ImageView image, String url) {

load(image, url, false);

}

public void load(ImageView image, String url, boolean cache) {

if (_urlToBitmap.get(url) != null) {

if(image!=null) {

image.setImageBitmap(_urlToBitmap.get(url));

}

} else {

image.setImageBitmap(null);

queue(image, url, cache);

}

}

public void queue(ImageView image, String url, boolean cache) {

Iterator it = _queue.iterator();

if (image!=null) {

while (it.hasNext()) {

if (it.next().image.equals(image)) {

it.remove();

break;

}

}

} else if (url!=null) {

while (it.hasNext()) {

if (it.next().url.equals(url)) {

it.remove();

break;

}

}

}

_queue.add(new Group(image, url, null, cache));

loadNext();

}

public void clearQueue() {

_queue = new LinkedList();

}

public void clearCache() {

_urlToBitmap = new HashMap();

}

public void cancel() {

clearQueue();

if ( _thread != null ) {

_thread.disconnect();

_thread = null;

}

}

public void setMissingBitmap(Bitmap bitmap) {

_missing = bitmap;

}

private void loadNext() {

Iterator it = _queue.iterator();

if (!_busy && it.hasNext() ) {

_busy = true;

Group group = it.next();

it.remove();

// double check image availability

if (_urlToBitmap.get(group.url) != null) {

if (group.image!=null) {

group.image.setImageBitmap(_urlToBitmap.get(group.url));

}

_busy = false;

loadNext();

} else {

_thread = new DownloadThread(group);

_thread.start();

}

}

}

private void onLoad() {

if (_thread != null) {

Group group = _thread.group;

if (group.bitmap != null) {

if (group.cache) {

_urlToBitmap.put(group.url, group.bitmap);

}

if (group.image != null) {

group.image.setImageBitmap(group.bitmap);

}

} else if (_missing != null) {

if (group.image != null) {

group.image.setImageBitmap(_missing);

}

}

}

_thread = null;

_busy = false;

loadNext();

}

private class Group {

public Group(ImageView image, String url, Bitmap bitmap, boolean cache) {

this.image = image;

this.url = url;

this.bitmap = bitmap;

this.cache = cache;

}

public ImageView image;

public String url;

public Bitmap bitmap;

public boolean cache;

}

private class DownloadThread extends Thread {

final Handler threadHandler = new Handler();

final Runnable threadCallback = new Runnable() {

public void run() {

onLoad();

}

};

private HttpURLConnection _conn;

public Group group;

public DownloadThread(Group group) {

this.group = group;

}

@Override

public void run() {

InputStream inStream = null;

_conn = null;

try {

_conn = (HttpURLConnection) new URL(group.url).openConnection();

_conn.setDoInput(true);

_conn.connect();

inStream = _conn.getInputStream();

group.bitmap = BitmapFactory.decodeStream(inStream);

inStream.close();

_conn.disconnect();

inStream = null;

_conn = null;

} catch (Exception ex) {

// nothing

}

if (inStream != null) {

try {

inStream.close();

} catch (Exception ex) {}

}

disconnect();

inStream = null;

_conn = null;

threadHandler.post(threadCallback);

}

public void disconnect() {

if (_conn != null) {

_conn.disconnect();

}

}

}

}

" wu-media.com

10 comments:

  1. Cool, but the HTML formatting strips away all the template classes.

    A nice extension would be to add a persistent cache using the cachemanager component.

    ReplyDelete
  2. Thank you for an excellent class. Just what I needed!

    Have a nice day!

    ReplyDelete
  3. Very awesome. This is a helpful post.

    ReplyDelete
  4. Thank you somuch for the code... it worked like a charm

    ReplyDelete
  5. You can also follow the popular banana diet, which entails eating a raw banana and drinking room temperature water for breakfast.
    There is no guideline provided as to what you can and cannot eat for lunch, dinner, and
    snacks. Information is power when it comes to weight loss plans and
    if you want to know more, the Internet provides a variety of informational resources that really can't be compared to.

    ReplyDelete
  6. Hi there very nice web site!! Guy .. Beautiful .
    . Amazing .. I will bookmark your web site
    and take the feeds additionally? I'm glad to find numerous helpful info here in the publish, we'd like develop
    extra techniques in this regard, thank you for sharing.

    . . . . .

    ReplyDelete
  7. I'm not sure why but this weblog is loading extremely slow for me. Is anyone else having this issue or is it a issue on my end? I'll
    check back later and see if the problem still exists.

    ReplyDelete
  8. Yesterday, while I was at work, my sister stole
    my iphone and tested to see if it can survive a thirty foot drop, just so she can be a youtube sensation.
    My apple ipad is now broken and she has 83 views.

    I know this is totally off topic but I had to share
    it with someone!

    ReplyDelete
  9. Highly energetic post, I enjoyed that bit. Will there be a
    part 2?

    ReplyDelete
  10. Hello! I could have sworn I've visited this web site before but
    after going through many of the articles I realized it's new to me.

    Anyways, I'm definitely delighted I discovered it and I'll
    be book-marking it and checking back often!

    ReplyDelete