com.twapime.app.util.AsyncImageLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.twapime.app.util.AsyncImageLoader.java

Source

/*
 * AsyncImageLoader.java
 * 25/05/2011
 * TwAPIme for Android
 * Copyright(c) Ernandes Mourao Junior (ernandes@gmail.com)
 * All rights reserved
 * GNU General Public License (GPL) Version 2, June 1991
 */
package com.twapime.app.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Environment;

/**
 * @author ernandes@gmail.com
 */
public final class AsyncImageLoader {
    /**
     * 
     */
    private static final int HARD_CACHE_CAPACITY = 20;

    /**
     * 
     */
    private static AsyncImageLoader singleton;

    /**
    * @param context
    * @return
    */
    public synchronized static AsyncImageLoader getInstance(Context context) {
        if (singleton == null) {
            singleton = new AsyncImageLoader(context);
        }
        //
        return singleton;
    }

    /**                                               
     * 
     */
    private final ConcurrentHashMap<String, SoftReference<Drawable>> softCache;

    /**
     * 
     */
    private final HashMap<String, Drawable> hardCache;

    /**
     * 
     */
    private final HashMap<String, List<ImageLoaderCallback>> callbacks;

    /**
     * 
     */
    private final List<String> urlsBeingDownloaded;

    /**
     * 
     */
    private File cacheDir;

    /**
     * 
     */
    private Context context;

    /**
     * @param context
     */
    private AsyncImageLoader(Context context) {
        this.context = context;
        //
        urlsBeingDownloaded = new Vector<String>();
        callbacks = new HashMap<String, List<ImageLoaderCallback>>();
        hardCache = new LinkedHashMap<String, Drawable>(HARD_CACHE_CAPACITY, 0.75f, true) {
            private static final long serialVersionUID = -4795168423098442895L;

            @Override
            protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Drawable> eldest) {
                if (size() > HARD_CACHE_CAPACITY) {
                    softCache.put(eldest.getKey(), new SoftReference<Drawable>(eldest.getValue()));
                    //
                    return true;
                } else
                    return false;
            }
        };
        softCache = new ConcurrentHashMap<String, SoftReference<Drawable>>(HARD_CACHE_CAPACITY / 2);
        //
        loadCacheDir();
    }

    /**
     * @param imageUrl
     * @param imageCallback
     * @return
     */
    public Drawable loadDrawable(final String imageUrl, ImageLoaderCallback imageCallback) {
        Drawable drawable = getDrawableFromCache(imageUrl);
        //
        if (drawable != null) {
            return drawable;
        }
        //
        synchronized (callbacks) {
            List<ImageLoaderCallback> callbacksList = callbacks.get(imageUrl);
            //
            if (callbacksList == null) {
                callbacksList = new Vector<ImageLoaderCallback>();
                callbacks.put(imageUrl, callbacksList);
            }
            //
            callbacksList.add(imageCallback);
        }
        //
        if (urlsBeingDownloaded.contains(imageUrl)) {
            return null;
        }
        //
        urlsBeingDownloaded.add(imageUrl);
        //
        new AsyncTask<String, Void, Drawable>() {
            @Override
            protected Drawable doInBackground(String... params) {
                return loadImageFromUrl(imageUrl);
            }

            @Override
            protected void onPostExecute(Drawable drawable) {
                if (drawable != null) {
                    addDrawableToCache(imageUrl, drawable);
                    //
                    synchronized (callbacks) {
                        for (ImageLoaderCallback callback : callbacks.get(imageUrl)) {
                            callback.imageLoaded(drawable, imageUrl);
                        }
                        //
                        callbacks.remove(imageUrl);
                    }
                }
                //
                urlsBeingDownloaded.remove(imageUrl);
            }
        }.execute();
        //
        return null;
    }

    /**
     * 
     */
    public void clearCache() {
        synchronized (hardCache) {
            hardCache.clear();
        }
        //
        softCache.clear();
        //
        for (File f : cacheDir.listFiles()) {
            f.delete();
        }
    }

    /**
    * @param imageUrl
    * @return
    */
    private Drawable getDrawableFromCache(String imageUrl) {
        synchronized (hardCache) {
            Drawable drawable = hardCache.get(imageUrl);
            //
            if (drawable != null) {
                // Drawable found in hard cache
                // Move element to first position, so that it is removed last
                hardCache.remove(imageUrl);
                hardCache.put(imageUrl, drawable);
                //
                return drawable;
            }
        }
        //
        SoftReference<Drawable> reference = softCache.get(imageUrl);
        //
        if (reference != null) {
            Drawable drawable = reference.get();
            //
            if (drawable != null) {
                return drawable;
            }
        }
        //
        return null;
    }

    /**
     * @param imageUrl
     * @param drawable
     */
    private void addDrawableToCache(String imageUrl, Drawable drawable) {
        if (drawable != null) {
            synchronized (hardCache) {
                hardCache.put(imageUrl, drawable);
            }
        }
    }

    /**
     * @param imageUrl
     * @return
     */
    private Drawable loadImageFromUrl(String imageUrl) {
        InputStream stream = null;
        //
        try {
            stream = readFromFile(imageUrl);
            //
            if (stream != null) {
                return Drawable.createFromStream(stream, "src");
            }
        } catch (IOException e) {
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                }
            }
        }
        //
        HttpClient client = new DefaultHttpClient();
        HttpGet req = new HttpGet(imageUrl);
        //
        try {
            HttpResponse res = client.execute(req);
            //
            if (res.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                return null;
            }
            //
            HttpEntity entity = res.getEntity();
            //
            if (entity != null) {
                try {
                    stream = new FlushedInputStream(entity.getContent());
                    //
                    writeToFile(imageUrl, stream);
                    stream.close();
                    stream = readFromFile(imageUrl);
                    //
                    return Drawable.createFromStream(stream, "src");
                } finally {
                    if (stream != null) {
                        stream.close();
                    }
                    //
                    entity.consumeContent();
                }
            }
        } catch (IOException e) {
            req.abort();
        }
        //
        return null;
    }

    /**
     * 
     */
    private void loadCacheDir() {
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            File sdDir = Environment.getExternalStorageDirectory();
            cacheDir = new File(sdDir.getAbsolutePath() + "/TwAPIme/cache");
            //
            if (!cacheDir.exists()) {
                cacheDir.mkdirs();
            }
        } else {
            cacheDir = context.getCacheDir();
        }
    }

    /**
     * @param imageUrl
     * @return
     * @throws IOException 
     */
    private InputStream readFromFile(String imageUrl) throws IOException {
        File file = getFileFromUrl(imageUrl);
        //
        if (file.exists()) {
            return new FileInputStream(file);
        } else {
            return null;
        }
    }

    /**
     * @param imageUrl
     * @param imageStream
     * @throws IOException 
     */
    private void writeToFile(String imageUrl, InputStream imageStream) throws IOException {
        File file = getFileFromUrl(imageUrl);
        //
        if (!file.exists()) {
            file.createNewFile();
        }
        //
        FileOutputStream o = new FileOutputStream(file);
        byte[] buffer = new byte[1024];
        int bytesRead;
        //
        while ((bytesRead = imageStream.read(buffer)) != -1) {
            o.write(buffer, 0, bytesRead);
        }
        //
        o.close();
    }

    /**
     * @param imageUrl
     * @return
     */
    private File getFileFromUrl(String imageUrl) {
        return new File(cacheDir, String.valueOf(imageUrl.hashCode()));
    }

    /**
      * <p>
      * An InputStream that skips the exact number of bytes provided, unless it
      * reaches EOF.
      * </p>
      * @author ernandes@gmail.com
      */
    private static class FlushedInputStream extends FilterInputStream {
        /**
         * @param inputStream
         */
        public FlushedInputStream(InputStream inputStream) {
            super(inputStream);
        }

        /**
         * @see java.io.FilterInputStream#skip(long)
         */
        @Override
        public long skip(long n) throws IOException {
            long totalBytesSkipped = 0L;
            //
            while (totalBytesSkipped < n) {
                long bytesSkipped = in.skip(n - totalBytesSkipped);
                //
                if (bytesSkipped == 0L) {
                    int b = read();
                    if (b < 0) {
                        break; // we reached EOF
                    } else {
                        bytesSkipped = 1; // we read one byte
                    }
                }
                //
                totalBytesSkipped += bytesSkipped;
            }
            //
            return totalBytesSkipped;
        }
    }

    /**
     * @author ernandes@gmail.com
     */
    public interface ImageLoaderCallback {
        public void imageLoaded(Drawable imageDrawable, String imageUrl);
    }
}