Android Open Source - Android-Lib-AsyncImageLoader Async Image Loader






From Project

Back to project page Android-Lib-AsyncImageLoader.

License

The source code is released under:

Apache License

If you think the Android project Android-Lib-AsyncImageLoader listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package android.lib.asyncimageloader;
//from ww  w.  java 2  s .c  om
import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.LinkedList;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.lib.net.HttpClient;
import android.media.ThumbnailUtils;
import android.widget.ImageView;

/**
 * An utility class for loading and displaying {@link Bitmap}s for {@link ImageView}s asynchronously.
 * <p>Note: {@link #close()} must be called to clean up the resources used by {@link AsyncImageLoader}.</p>
 */
public final class AsyncImageLoader {
    private static BitmapCache bitmapCache;

    private final LinkedList<WorkItem> workItems = new LinkedList<WorkItem>();
    private final DiskCache            diskCache;
    private final Activity             activity;

    private boolean paused;
    private boolean closed;
    private Thread  thread;

    /**
     * Creates a new instance of {@link AsyncImageLoader} object and specifies the maximum number of
     * {@link Bitmap} objects to cache in memory, and the path to store generated thumbnails.
     * @param activity the {@link Activity} that hosts the {@link ImageView} to display loaded {@link Bitmap}s.
     * @param numberOfCachedBitmaps the maximum number of {@link Bitmap} objects to cache in memory.
     * @param thumbnailPath the path to store generated thumbnail files.
     */
    public AsyncImageLoader(final Activity activity, final int numberOfCachedBitmaps, final String thumbnailPath) {
        if (AsyncImageLoader.bitmapCache == null) {
            AsyncImageLoader.bitmapCache = new BitmapCache(numberOfCachedBitmaps);
        }

        this.diskCache = new DiskCache(thumbnailPath);
        this.activity  = activity;
    }

    /**
     * Loads and displays a {@link Bitmap} at the given <code>path</code> for an {@link ImageView}.
     * @param path the path to load a {@link Bitmap}.
     * @param imageView the {@link ImageView} to display the loaded {@link Bitmap}.
     */
    public void loadImage(final String path, final ImageView imageView) {
        this.loadImage(path, imageView, null, null);
    }

    /**
     * Loads and displays a {@link Bitmap} from the given <code>url</code> for an {@link ImageView}.
     * @param url the URL to retrieve the {@link Bitmap} data.
     * @param imageView the {@link ImageView} to display the loaded {@link Bitmap}.
     */
    public void loadImage(final URL url, final ImageView imageView) {
        this.loadImage(url, imageView, null);
    }

    /**
     * Loads and displays a {@link Bitmap} at the given <code>path</code> for an {@link ImageView}.
     * @param path the path to load a {@link Bitmap}.
     * @param imageView the {@link ImageView} to display the loaded {@link Bitmap}.
     * @param options the bitmap options to use, if any,  when loading the {@link Bitmap}.
     */
    public void loadImage(final String path, final ImageView imageView, final BitmapFactory.Options options) {
        this.loadImage(path, imageView, options, null);
    }

    /**
     * Loads and displays a {@link Bitmap} at the given <code>path</code> for an {@link ImageView},
     * and notifies the given <code>listener</code> when the process completes.
     * @param path the path to load a {@link Bitmap}.
     * @param imageView the {@link ImageView} to display the loaded {@link Bitmap}.
     * @param listener a callback when the process completes.
     */
    public void loadImage(final String path, final ImageView imageView, final OnImageLoadedListener listener) {
        this.loadImage(path, imageView, null, listener);
    }

    /**
     * Loads and displays a {@link Bitmap} at the given <code>path</code> for an {@link ImageView},
     * and notifies the given <code>listener</code> when the process completes.
     * @param path the path to load a {@link Bitmap}.
     * @param imageView the {@link ImageView} to display the loaded {@link Bitmap}.
     * @param options the bitmap options to use, if any,  when loading the {@link Bitmap}.
     * @param listener a callback when the process completes.
     */
    public void loadImage(final String path, final ImageView imageView, final BitmapFactory.Options options, final OnImageLoadedListener listener) {
        this.checkState();

        if (this.thread == null) {
            this.thread = new Thread(new WorkerThread());

            this.thread.start();
        }

        this.cancel(imageView);

        synchronized(this.workItems) {
            this.workItems.add(new WorkItem(path, imageView, options, -1, -1, listener));
            this.workItems.notifyAll();
        }
    }

    /**
     * Loads and displays a {@link Bitmap} from the given <code>url</code> for an {@link ImageView},
     * and notifies the given <code>listener</code> when the process completes.
     * @param url the URL to retrieve the {@link Bitmap} data.
     * @param imageView the {@link ImageView} to display the loaded {@link Bitmap}.
     * @param listener a callback when the process completes.
     */
    public void loadImage(final URL url, final ImageView imageView, final OnImageLoadedListener listener) {
        this.checkState();

        if (this.thread == null) {
            this.thread = new Thread(new WorkerThread());

            this.thread.start();
        }

        this.cancel(imageView);

        synchronized(this.workItems) {
            this.workItems.add(new WorkItem(url, imageView, null, -1, -1, listener));
            this.workItems.notifyAll();
        }
    }

    /**
     * Loads and displays the thumbnail version of the {@link Bitmap} at the given <code>path</code>
     * for an {@link ImageView}.
     * @param path the path to create a thumbnail from.
     * @param imageView the {@link ImageView} to display the generated thumbnail.
     * @param kind either {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND} or
     * {@link android.provider.MediaStore.Images.Thumbnails#MICRO_KIND}, or -1 if this parameter is irrelevant.
     */
    public void loadImageThumbnail(final String path, final ImageView imageView, final int kind) {
        this.loadImageThumbnail(path, imageView, kind, null);
    }

    /**
     * Loads and displays the thumbnail version of the {@link Bitmap} at the given <code>path</code>
     * for an {@link ImageView}.
     * @param path the path to create a thumbnail from.
     * @param imageView the {@link ImageView} to display the generated thumbnail.
     * @param kind either {@link android.provider.MediaStore.Images.Thumbnails#MINI_KIND} or
     * {@link android.provider.MediaStore.Images.Thumbnails#MICRO_KIND}, or -1 if this parameter is irrelevant.
     * @param listener a callback when the process completes.
     */
    public void loadImageThumbnail(final String path, final ImageView imageView, final int kind, final OnImageLoadedListener listener) {
        this.checkState();

        if (this.thread == null) {
            this.thread = new Thread(new WorkerThread());

            this.thread.start();
        }

        this.cancel(imageView);

        synchronized (this.workItems) {
            this.workItems.add(new WorkItem(path, imageView, null, kind, -1, listener));
            this.workItems.notifyAll();
        }
    }

    /**
     * Loads and displays the thumbnail of a video at the given <code>path</code> for an {@link ImageView}.
     * @param path the path to create a thumbnail from.
     * @param imageView the {@link ImageView} to display the generated thumbnail.
     * @param kind either {@link android.provider.MediaStore.Video.Thumbnails#MINI_KIND} or
     * {@link android.provider.MediaStore.Video.Thumbnails#MICRO_KIND}, or -1 if this parameter is irrelevant.
     */
    public void loadVideoThumbnail(final String path, final ImageView imageView, final int kind) {
        this.loadVideoThumbnail(path, imageView, kind, null);
    }

    /**
     * Loads and displays the thumbnail of a video at the given <code>path</code> for an {@link ImageView}.
     * @param path the path to create a thumbnail from.
     * @param imageView the {@link ImageView} to display the generated thumbnail.
     * @param kind either {@link android.provider.MediaStore.Video.Thumbnails#MINI_KIND} or
     * {@link android.provider.MediaStore.Video.Thumbnails#MICRO_KIND}, or -1 if this parameter is irrelevant.
     * @param listener a callback when the process completes.
     */
    public void loadVideoThumbnail(final String path, final ImageView imageView, final int kind, final OnImageLoadedListener listener) {
        this.checkState();

        if (this.thread == null) {
            this.thread = new Thread(new WorkerThread());

            this.thread.start();
        }

        this.cancel(imageView);

        synchronized (this.workItems) {
            this.workItems.add(new WorkItem(path, imageView, null, -1, kind, listener));
            this.workItems.notifyAll();
        }
    }

    /**
     * Pauses the thread that loads and displays images.
     */
    public void pause() {
        synchronized (this.workItems) {
            this.paused = true;

            this.workItems.notifyAll();
        }
    }

    /**
     * Resumes any paused thread that loads and displays images.
     */
    public void resume() {
        synchronized (this.workItems) {
            this.paused = false;

            this.workItems.notifyAll();
        }
    }

    /**
     * Cleans up any resources used by the loader.
     * <p>Further use of the loader beyond this point may cause unexpected errors.</p>
     */
    public void close() {
        if (!this.closed) {
            synchronized (this.workItems) {
                this.closed = true;

                this.workItems.notifyAll();
            }

            this.workItems.clear();

            try {
                this.thread.join();
            } catch (final InterruptedException e) {
            }
        }
    }

    private void cancel(final ImageView imageView) {
        synchronized (this.workItems) {
            WorkItem workItemToRemove = null;

            for (final WorkItem workItem : this.workItems) {
                if (workItem.imageView == imageView) {
                    workItemToRemove = workItem;

                    break;
                }
            }

            if (workItemToRemove != null) {
                this.workItems.remove(workItemToRemove);
            }
        }
    }

    private void checkState() {
        if (this.closed) {
            throw new IllegalStateException("AsyncImageLoaded has already been closed"); //$NON-NLS-1$
        }
    }

    private final class WorkerThread implements Runnable {
        public WorkerThread() {
        }

        @SuppressWarnings("synthetic-access")
        @Override
        public void run() {
            while (true) {
                WorkItem workItem = null;

                synchronized (AsyncImageLoader.this.workItems) {
                    if (AsyncImageLoader.this.closed) {
                        break;
                    }

                    if (AsyncImageLoader.this.paused) {
                        try {
                            AsyncImageLoader.this.workItems.wait();
                        } catch (final InterruptedException e) {
                        }
                    }

                    if (AsyncImageLoader.this.workItems.isEmpty()) {
                        try {
                            AsyncImageLoader.this.workItems.wait();
                        } catch (final InterruptedException e) {
                        }

                        continue;
                    }

                    workItem = AsyncImageLoader.this.workItems.removeFirst();
                }

                if (workItem != null) {
                    Bitmap bitmap = null;

                    final SoftReference<Bitmap> reference = AsyncImageLoader.bitmapCache.get(workItem.path == null ? workItem.url.toString() : workItem.path);

                    if (reference != null) {
                        bitmap = reference.get();
                    }

                    if (bitmap == null) {
                        try {
                            if (workItem.path == null) {
                                final byte[] data = HttpClient.get(workItem.url.toString());

                                bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
                            } else {
                                if (workItem.imageKind > 0) {
                                    bitmap = AsyncImageLoader.this.diskCache.get(workItem.path, workItem.imageKind);
                                } else if (workItem.videoKind > 0) {
                                    bitmap = ThumbnailUtils.createVideoThumbnail(workItem.path, workItem.videoKind);
                                } else {
                                    bitmap = AsyncImageLoader.this.diskCache.get(workItem.path);

                                    if (bitmap == null) {
                                        bitmap = BitmapFactory.decodeFile(workItem.path, workItem.options);
                                    }
                                }
                            }

                            if (bitmap != null) {
                                AsyncImageLoader.bitmapCache.put(workItem.path == null ? workItem.url.toString() : workItem.path, new SoftReference<Bitmap>(bitmap));

                                if (workItem.path != null && AsyncImageLoader.this.diskCache.has(workItem.path)) {
                                    AsyncImageLoader.this.diskCache.put(workItem.path, bitmap);
                                }

                                this.onImageLoaded(workItem, bitmap);
                            }
                        } catch (final Exception e) {
                            if (workItem.listener != null) {
                                workItem.listener.onError(workItem.imageView, e);
                            }
                        }
                    } else {
                        this.onImageLoaded(workItem, bitmap);
                    }
                }
            }
        }

        @SuppressWarnings("synthetic-access")
        private void onImageLoaded(final WorkItem workItem, final Bitmap bitmap) {
            if (workItem.listener != null) {
                final Bitmap finalBitmap = workItem.listener.onImageLoaded(workItem.imageView, bitmap);

                if (workItem.imageView != null) {
                    AsyncImageLoader.this.activity.runOnUiThread(new UpdateImageView(workItem.imageView, finalBitmap));
                }
            } else {
                if (workItem.imageView != null) {
                    AsyncImageLoader.this.activity.runOnUiThread(new UpdateImageView(workItem.imageView, bitmap));
                }
            }
        }
    }
}




Java Source Code List

android.lib.asyncimageloader.AsyncImageLoader.java
android.lib.asyncimageloader.BitmapCache.java
android.lib.asyncimageloader.DiskCache.java
android.lib.asyncimageloader.GalleryAdapter.java
android.lib.asyncimageloader.OnImageLoadedListener.java
android.lib.asyncimageloader.PauseOnScroll.java
android.lib.asyncimageloader.UpdateImageView.java
android.lib.asyncimageloader.WorkItem.java