org.mythdroid.cache.ImageCache.java Source code

Java tutorial

Introduction

Here is the source code for org.mythdroid.cache.ImageCache.java

Source

/*
MythDroid: Android MythTV Remote
Copyright (C) 2009-2010 foobum@gmail.com
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.mythdroid.cache;

import java.io.IOException;
import java.util.LinkedList;
import java.util.NoSuchElementException;

import org.mythdroid.util.LogUtil;

import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

/**
 * A two level cache for images.
 * The first level is a MemCache, the second level is an ImageDiskCache.
 * Writes to the second level are lazy
 */
public class ImageCache {

    final private LinkedList<Runnable> queue = new LinkedList<Runnable>();

    private Thread diskCacheThread = null;
    private boolean runDiskCacheThread = true;

    private LruCache<String, Bitmap> memCache = null;
    private ImageDiskCache diskCache = null;
    private long memMax = 0;

    /**
     * Constructor
     * @param name name of the cache
     * @param memCapacity maximum capacity of MemCache in bytes
     * @param memMaxSize maximum size of image to cache in memory in bytes
     * @param diskMaxSize maximum size of disk backed cache in bytes
     */
    public ImageCache(String name, long memCapacity, long memMaxSize, int diskMaxSize) {
        try {
            diskCache = new ImageDiskCache(name, diskMaxSize);
        } catch (IOException e) {
            LogUtil.debug("Disk cache disabled: " + e.getMessage()); //$NON-NLS-1$
        }

        if (diskCache != null && diskCacheThread == null)
            newDiskCacheThread();

        memCache = new LruCache<String, Bitmap>((int) memCapacity) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getRowBytes() * value.getHeight();
            }
        };

        memMax = memMaxSize;

    }

    /**
     * Add an image to the cache
     * @param key identifier
     * @param value image to cache
     */
    public void put(final String key, final Bitmap value) {
        final String k = normalise(key);
        if (memMax == 0 || value.getRowBytes() * value.getHeight() <= memMax)
            memCache.put(k, value);
        if (diskCache != null) {
            synchronized (queue) {
                queue.add(new Runnable() {
                    @Override
                    public void run() {
                        diskCache.put(k, value);
                    }
                });
                queue.notify();
            }
        }
    }

    /**
     * Retrieve an image from the cache
     * @param key identifier
     * @return the cached image
     */
    public Bitmap get(final String key) {
        final String k = normalise(key);
        Bitmap ret = memCache.get(k);
        if (ret != null || diskCache == null)
            return ret;
        ret = diskCache.get(k);
        if (ret != null)
            memCache.put(k, ret);
        return ret;
    }

    /** Clean up internal resources employed by the ImageCache */
    public void shutdown() {
        runDiskCacheThread = false;
        if (diskCacheThread == null) {
            runDiskCacheThread = true;
            return;
        }
        diskCacheThread.interrupt();
        try {
            diskCacheThread.join();
        } catch (InterruptedException e) {
        }
        diskCacheThread = null;
        runDiskCacheThread = true;
        memCache.evictAll();
    }

    private void newDiskCacheThread() {
        diskCacheThread = new Thread(new Runnable() {
            @Override
            public void run() {

                Runnable r = null;
                while (runDiskCacheThread) {

                    synchronized (queue) {
                        while (queue.isEmpty() && runDiskCacheThread)
                            try {
                                queue.wait();
                            } catch (InterruptedException e) {
                            }
                        try {
                            r = queue.removeFirst();
                        } catch (NoSuchElementException e) {
                            continue;
                        }
                    }

                    try {
                        r.run();
                    } catch (RuntimeException e) {
                        LogUtil.debug("Exception in diskCacheThread: " + e.getMessage() //$NON-NLS-1$
                        );
                    }
                    r = null;

                }

            }
        });
        diskCacheThread.setName("diskCache"); //$NON-NLS-1$
        diskCacheThread.setDaemon(true);
        diskCacheThread.setPriority(Thread.MIN_PRIORITY);
        diskCacheThread.start();
    }

    /** Normalise a URL by removing protocol strings and bad characters */
    private String normalise(final String key) {

        int i = 0;

        if (key.startsWith("http://")) //$NON-NLS-1$
            i = key.indexOf('/', 7);

        char[] array = key.toCharArray();
        int size = array.length;
        char[] dest = new char[size];

        int j = 0;
        for (; i < size; i++) {
            switch (array[i]) {
            case '/':
            case '&':
            case '?':
            case ';':
            case ':':
                continue;
            default:
                dest[j++] = array[i];
            }
        }

        return String.valueOf(dest, 0, j);

    }

}