com.secretlisa.lib.utils.BaseImageLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.secretlisa.lib.utils.BaseImageLoader.java

Source

/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.secretlisa.lib.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
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.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.text.TextUtils;
import android.widget.ImageView;

import com.secretlisa.lib.http.HttpRequest;
import com.secretlisa.lib.http.NetworkUtil;
import com.secretlisa.lib.http.Response;
import com.secretlisa.lib.http.SecretLisaException;

public abstract class BaseImageLoader {

    private static final String TAG = "ImageDownloaderBase";

    public static int NET_TYPE;

    private Context mContext;

    public BaseImageLoader(Context mContext) {
        super();
        this.mContext = mContext;
    }

    public void download(String url, ImageView imageView) {
        resetPurgeTimer();
        Bitmap bitmap = getBitmapFromCache(url);

        if (bitmap == null) {
            Log.v(TAG, "bitmap is null,download from web");
            forceDownload(url, imageView);
        } else {
            Log.v(TAG, "bitmap is not null");
            cancelPotentialDownload(url, imageView);
            imageView.setImageBitmap(bitmap);
        }
    }

    private void forceDownload(String url, ImageView imageView) {
        if (url == null) {
            imageView.setImageDrawable(null);
            return;
        }

        if (cancelPotentialDownload(url, imageView)) {
            BitmapDownloaderTask task = null;
            task = new BitmapDownloaderTask(imageView, url);
            Object obj = imageView.getTag();
            int resId = -1;
            Bitmap bitmapDefault = null;
            if (obj != null) {
                resId = (Integer) obj;
                bitmapDefault = BitmapFactory.decodeResource(imageView.getResources(), resId);
            }
            DownloadedDrawable downloadedDrawable = new DownloadedDrawable(bitmapDefault, task);
            imageView.setImageDrawable(downloadedDrawable);
            // imageView.setBackgroundColor(Color.WHITE);
            //         imageView.setBackgroundResource(R.drawable.wallpaper_bg);
            imageView.setMinimumHeight(156);
            task.execute();
        }
    }

    private boolean cancelPotentialDownload(String url, ImageView imageView) {
        BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
        if (bitmapDownloaderTask != null) {
            String bitmapUrl = bitmapDownloaderTask.imageUrl;
            if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
                bitmapDownloaderTask.cancelTask();
            } else {
                return false;
            }
        }
        return true;
    }

    private BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
        if (imageView != null) {
            Drawable drawable = imageView.getDrawable();
            if (drawable instanceof DownloadedDrawable) {
                DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
                return downloadedDrawable.getBitmapDownloaderTask();
            }
        }
        return null;
    }

    public Bitmap downloadBitmap(final String imageUrl) {
        // AndroidHttpClient is not allowed to be used from the main thread
        final HttpClient client = new DefaultHttpClient();
        // ??
        int netType = NetworkUtil.getNetworkType(mContext);
        if (netType == NetworkUtil.TYPE_WAP) {
            String proxyHost = android.net.Proxy.getDefaultHost();
            if (proxyHost != null) {
                HttpHost proxy = new HttpHost(proxyHost, android.net.Proxy.getDefaultPort());
                client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
            }
        }
        final HttpGet getRequest = new HttpGet(imageUrl);
        try {
            HttpResponse response = client.execute(getRequest);
            final int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                return null;
            }
            final HttpEntity entity = response.getEntity();
            if (entity != null) {
                try {
                    final byte[] respBytes = getBytes(entity.getContent());
                    writeImageFile(imageUrl, respBytes);
                    // Decode the bytes and return the bitmap.
                    return BitmapFactory.decodeByteArray(respBytes, 0, respBytes.length, null);
                } finally {
                    entity.consumeContent();
                }

            }
        } catch (IOException e) {
            getRequest.abort();
        } catch (OutOfMemoryError e) {
            clearCache();
            System.gc();
        } catch (IllegalStateException e) {
            getRequest.abort();
        } catch (Exception e) {
            getRequest.abort();
        } finally {
        }
        return null;
    }

    public Bitmap downloadBitmapFromWeb(String imageUrl) {
        HttpRequest request = new HttpRequest(mContext);
        Response response = null;
        try {
            response = request.httpRequest(imageUrl, "GET", null, null);
            final byte[] respBytes = getBytes(response.getInputStream());
            writeImageFile(imageUrl, respBytes);
            // Decode the bytes and return the bitmap.
            return BitmapFactory.decodeByteArray(respBytes, 0, respBytes.length, null);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                response.disconnect();
            }
        }
        return null;
    }

    public byte[] getBytes(InputStream bis) throws SecretLisaException {
        byte[] content = null;
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[4096];
            int length = 0;
            while ((length = bis.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            content = baos.toByteArray();
            if (content.length == 0) {
                content = null;
            }
            baos.close();
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return content;
    }

    private synchronized void writeImageFile(final String imageUrl, final byte[] respBytes) throws IOException {
        String hashedUrl = CommonUtil.md5(imageUrl);
        FileOutputStream fos = null;
        try {
            if (FileUtil.isSdcardValid()) {
                Log.d(TAG, "sdcard valid");
                fos = new FileOutputStream(new File(FileUtil.getFolderPath(getCachePath()) + hashedUrl));
            } else {
                Log.d(TAG, "sdcard not valid");
                fos = mContext.openFileOutput(hashedUrl, Context.MODE_PRIVATE);
            }
            fos.write(respBytes);
        } catch (FileNotFoundException e) {
            Log.w(TAG, "Error writing to bitmap cache: " + imageUrl, e);
        } catch (IOException e) {
            Log.w(TAG, "Error writing to bitmap cache: " + imageUrl, e);
        } finally {
            if (fos != null) {
                fos.close();
            }
        }
    }

    /**
     * The actual AsyncTask that will asynchronously download the image.
     */
    class BitmapDownloaderTask extends BaseTask<Bitmap, String> {
        private String imageUrl;
        private final WeakReference<ImageView> imageViewReference;

        public BitmapDownloaderTask(ImageView imageView, String url) {
            imageViewReference = new WeakReference<ImageView>(imageView);
            imageUrl = url;
        }

        /**
         * Actual download method.
         */
        @Override
        protected Bitmap doInBackground() {
            if (isCanceled()) {
                return null;
            }
            if (!TextUtils.isEmpty(imageUrl)) {
                // first look from local file.
                Bitmap bitmap = getBitmapFromCacheFile(imageUrl);
                // download from web server
                if (bitmap == null) {
                    if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.GINGERBREAD) {
                        bitmap = downloadBitmap(imageUrl);
                    } else {
                        bitmap = downloadBitmapFromWeb(imageUrl);
                    }
                }
                if (bitmap != null) {
                    addBitmapToCache(imageUrl, bitmap);
                    return bitmap;
                }
            }
            return null;
        }

        /**
         * Once the image is downloaded, associates it to the imageView
         */
        @Override
        protected void onPostTask(Bitmap bitmap) {
            super.onPostTask(bitmap);
            if (bitmap == null) {
                return;
            }
            if (imageViewReference != null) {
                ImageView imageView = imageViewReference.get();
                if (imageView != null)
                    imageView.setBackgroundDrawable(null);
                BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
                if ((this == bitmapDownloaderTask) /* || (mode != Mode.CORRECT) */) {
                    if (imageView != null) {
                        imageView.setImageBitmap(bitmap);
                    }
                }
            }
        }

        @Override
        protected void onPreTask() {
            super.onPreTask();
        }

    }

    /**
     * A fake Drawable that will be attached to the imageView while the download
     * is in progress.
     * 
     * <p>
     * Contains a reference to the actual download task, so that a download task
     * can be stopped if a new binding is required, and makes sure that only the
     * last started download process can bind its result, independently of the
     * download finish order.
     * </p>
     */
    class DownloadedDrawable extends BitmapDrawable {

        private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

        public DownloadedDrawable(Bitmap bitmap, BitmapDownloaderTask bitmapDownloaderTask) {
            super(bitmap);
            bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
        }

        public BitmapDownloaderTask getBitmapDownloaderTask() {
            return bitmapDownloaderTaskReference.get();
        }
    }

    /*
     * Cache-related fields and methods.
     * 
     * We use a hard and a soft cache. A soft reference cache is too
     * aggressively cleared by the Garbage Collector.
     */

    private static final int HARD_CACHE_CAPACITY = 10;

    private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds

    // Hard cache, with a fixed maximum capacity and a life duration
    private final HashMap<String, Bitmap> sHardBitmapCache = new LinkedHashMap<String, Bitmap>(
            HARD_CACHE_CAPACITY / 2, 0.75f, true) {

        @Override
        protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
            if (size() > HARD_CACHE_CAPACITY) {
                // Entries push-out of hard reference cache are transferred to
                // soft reference cache
                sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
                return true;
            } else
                return false;
        }
    };

    private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache = new ConcurrentHashMap<String, SoftReference<Bitmap>>(
            HARD_CACHE_CAPACITY / 2);

    private final Handler purgeHandler = new Handler();

    private final Runnable purger = new Runnable() {

        public void run() {
            clearCache();
        }
    };

    /**
     * Adds this bitmap to the cache.
     * 
     * @param bitmap
     *            The newly downloaded bitmap.
     */
    private void addBitmapToCache(String url, Bitmap bitmap) {
        if (bitmap != null) {
            synchronized (sHardBitmapCache) {
                sHardBitmapCache.put(url, bitmap);
            }
        }
    }

    /**
     * @param url
     *            The URL of the image that will be retrieved from the cache.
     * @return The cached bitmap or null if it was not found.
     */
    private Bitmap getBitmapFromCache(String url) {
        if (TextUtils.isEmpty(url)) {
            return null;
        }

        // First try the hard reference cache
        synchronized (sHardBitmapCache) {
            final Bitmap bitmap = sHardBitmapCache.get(url);
            if (bitmap != null) {
                // Bitmap found in hard cache
                // Move element to first position, so that it is removed last
                sHardBitmapCache.remove(url);
                sHardBitmapCache.put(url, bitmap);
                return bitmap;
            }
        }

        // Then try the soft reference cache
        SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
        if (bitmapReference != null) {
            final Bitmap bitmap = bitmapReference.get();
            if (bitmap != null) {
                // Bitmap found in soft cache
                return bitmap;
            } else {
                // Soft reference has been Garbage Collected
                sSoftBitmapCache.remove(url);
            }
        }

        Log.d(TAG, "look from local file:" + url);
        // look from local file.
        Bitmap bitmap = getBitmapFromCacheFile(url);

        if (bitmap != null) {
            addBitmapToCache(url, bitmap);
        }
        return bitmap;
    }

    // Looks to see if an image is in the file system.
    public synchronized Bitmap getBitmapFromCacheFile(String url) {
        String hashedUrl = CommonUtil.md5(url);
        Log.d(TAG, "file md5 = " + hashedUrl);
        FileInputStream fis = null;
        try {
            if (FileUtil.isSdcardValid()
                    && FileUtil.fileExist(FileUtil.getFolderPath(getCachePath()) + hashedUrl)) {
                Log.d(TAG, "look from external storage");
                fis = new FileInputStream(new File(FileUtil.getFolderPath(getCachePath()) + hashedUrl));
            } else {
                Log.d(TAG, "look from internal storage");
                fis = mContext.openFileInput(hashedUrl);
            }
            return BitmapFactory.decodeStream(fis);
        } catch (Exception e) {
            Log.e(TAG, "error", e);
            // Not there.
            return null;
        } catch (OutOfMemoryError e) {
            // Not there.
            System.gc();
            clearCache();
            return null;
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    // Ignore.
                }
            }
        }
    }

    /**
     * Clears the image cache used internally to improve performance. Note that
     * for memory efficiency reasons, the cache will automatically be cleared
     * after a certain inactivity delay.
     */
    public void clearCache() {
        sHardBitmapCache.clear();
        sSoftBitmapCache.clear();
    }

    /**
     * Allow a new delay before the automatic cache clear is done.
     */
    private void resetPurgeTimer() {
        purgeHandler.removeCallbacks(purger);
        purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE);
    }

    protected abstract String getCachePath();
}