diff options
author | daniel oeh <daniel.oeh@gmail.com> | 2014-03-19 18:01:55 +0100 |
---|---|---|
committer | daniel oeh <daniel.oeh@gmail.com> | 2014-03-19 18:01:55 +0100 |
commit | b9ab10253ab04bacd7d27ab7a7aecd561f7efbc2 (patch) | |
tree | 335d19a5d4399c6b2a057915afd23960e297b90f | |
parent | dfa10a5989ad3b99dbd4f32742aa76de448af92c (diff) | |
download | AntennaPod-b9ab10253ab04bacd7d27ab7a7aecd561f7efbc2.zip |
Prevent images from being loaded multiple times
(copied from AntennaPodSP)
-rw-r--r-- | src/de/danoeh/antennapod/asynctask/ImageLoader.java | 436 |
1 files changed, 222 insertions, 214 deletions
diff --git a/src/de/danoeh/antennapod/asynctask/ImageLoader.java b/src/de/danoeh/antennapod/asynctask/ImageLoader.java index a4a9bc823..c49d7549b 100644 --- a/src/de/danoeh/antennapod/asynctask/ImageLoader.java +++ b/src/de/danoeh/antennapod/asynctask/ImageLoader.java @@ -1,14 +1,8 @@ package de.danoeh.antennapod.asynctask; -import java.io.InputStream; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.Context; -import android.content.res.TypedArray; import android.os.Handler; import android.support.v4.util.LruCache; import android.util.Log; @@ -17,214 +11,228 @@ import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.R; -/** Caches and loads FeedImage bitmaps in the background */ +import java.io.InputStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +/** + * Caches and loads FeedImage bitmaps in the background + */ public class ImageLoader { - private static final String TAG = "ImageLoader"; - private static ImageLoader singleton; - - public static final int IMAGE_TYPE_THUMBNAIL = 0; - public static final int IMAGE_TYPE_COVER = 1; - - private Handler handler; - private ExecutorService executor; - - /** - * Stores references to loaded bitmaps. Bitmaps can be accessed by the id of - * the FeedImage the bitmap belongs to. - */ - - final int memClass = ((ActivityManager) PodcastApp.getInstance() - .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); - - // Use 1/8th of the available memory for this memory cache. - final int thumbnailCacheSize = 1024 * 1024 * memClass / 8; - - private LruCache<String, CachedBitmap> coverCache; - private LruCache<String, CachedBitmap> thumbnailCache; - - private ImageLoader() { - handler = new Handler(); - executor = createExecutor(); - - coverCache = new LruCache<String, CachedBitmap>(1); - - thumbnailCache = new LruCache<String, CachedBitmap>(thumbnailCacheSize) { - - @SuppressLint("NewApi") - @Override - protected int sizeOf(String key, CachedBitmap value) { - if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= 12) - return value.getBitmap().getByteCount(); - else - return (value.getBitmap().getRowBytes() * value.getBitmap() - .getHeight()); - - } - - }; - } - - private ExecutorService createExecutor() { - return Executors.newFixedThreadPool(Runtime.getRuntime() - .availableProcessors(), new ThreadFactory() { - - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setPriority(Thread.MIN_PRIORITY); - return t; - } - }); - } - - public static synchronized ImageLoader getInstance() { - if (singleton == null) { - singleton = new ImageLoader(); - } - return singleton; - } - - /** - * Load a bitmap from the cover cache. If the bitmap is not in the cache, it - * will be loaded from the disk. This method should either be called if the - * ImageView's size has already been set or inside a Runnable which is - * posted to the ImageView's message queue. - */ - public void loadCoverBitmap(ImageWorkerTaskResource source, ImageView target) { - loadCoverBitmap(source, target, target.getHeight()); - } - - /** - * Load a bitmap from the cover cache. If the bitmap is not in the cache, it - * will be loaded from the disk. This method should either be called if the - * ImageView's size has already been set or inside a Runnable which is - * posted to the ImageView's message queue. - */ - public void loadCoverBitmap(ImageWorkerTaskResource source, - ImageView target, int length) { - final int defaultCoverResource = getDefaultCoverResource(target - .getContext()); - - if (source != null && source.getImageLoaderCacheKey() != null) { - target.setTag(R.id.imageloader_key, source.getImageLoaderCacheKey()); - CachedBitmap cBitmap = getBitmapFromCoverCache(source.getImageLoaderCacheKey()); - if (cBitmap != null && cBitmap.getLength() >= length) { - target.setImageBitmap(cBitmap.getBitmap()); - } else { - target.setImageResource(defaultCoverResource); - BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( - handler, target, source, length, IMAGE_TYPE_COVER); - executor.submit(worker); - } - } else { - target.setImageResource(defaultCoverResource); - } - } - - /** - * Load a bitmap from the thumbnail cache. If the bitmap is not in the - * cache, it will be loaded from the disk. This method should either be - * called if the ImageView's size has already been set or inside a Runnable - * which is posted to the ImageView's message queue. - */ - public void loadThumbnailBitmap(ImageWorkerTaskResource source, - ImageView target) { - loadThumbnailBitmap(source, target, target.getHeight()); - } - - /** - * Load a bitmap from the thumbnail cache. If the bitmap is not in the - * cache, it will be loaded from the disk. This method should either be - * called if the ImageView's size has already been set or inside a Runnable - * which is posted to the ImageView's message queue. - */ - public void loadThumbnailBitmap(ImageWorkerTaskResource source, - ImageView target, int length) { - final int defaultCoverResource = getDefaultCoverResource(target - .getContext()); - - if (source != null && source.getImageLoaderCacheKey() != null) { - target.setTag(R.id.imageloader_key, source.getImageLoaderCacheKey()); - CachedBitmap cBitmap = getBitmapFromThumbnailCache(source.getImageLoaderCacheKey()); - if (cBitmap != null && cBitmap.getLength() >= length) { - target.setImageBitmap(cBitmap.getBitmap()); - } else { - target.setImageResource(defaultCoverResource); - BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( - handler, target, source, length, IMAGE_TYPE_THUMBNAIL); - executor.submit(worker); - } - } else { - target.setImageResource(defaultCoverResource); - } - } - - public void clearExecutorQueue() { - executor.shutdownNow(); - if (AppConfig.DEBUG) - Log.d(TAG, "Executor was shut down."); - executor = createExecutor(); - - } - - public void wipeImageCache() { - coverCache.evictAll(); - thumbnailCache.evictAll(); - } - - public boolean isInThumbnailCache(String fileUrl) { - return thumbnailCache.get(fileUrl) != null; - } - - private CachedBitmap getBitmapFromThumbnailCache(String key) { - return thumbnailCache.get(key); - } - - public void addBitmapToThumbnailCache(String key, CachedBitmap bitmap) { - thumbnailCache.put(key, bitmap); - } - - public boolean isInCoverCache(String fileUrl) { - return coverCache.get(fileUrl) != null; - } - - private CachedBitmap getBitmapFromCoverCache(String key) { - return coverCache.get(key); - } - - public void addBitmapToCoverCache(String key, CachedBitmap bitmap) { - coverCache.put(key, bitmap); - } - - private int getDefaultCoverResource(Context context) { - return android.R.color.transparent; - } - - /** - * Used by the BitmapDecodeWorker task to retrieve the source of the bitmap. - */ - public interface ImageWorkerTaskResource { - /** - * Opens a new InputStream that can be decoded as a bitmap by the - * BitmapFactory. - */ - public InputStream openImageInputStream(); - - /** - * Returns an InputStream that points to the beginning of the image - * resource. Implementations can either create a new InputStream or - * reset the existing one, depending on their implementation of - * openInputStream. If a new InputStream is returned, the one given as a - * parameter MUST be closed. - * @param input The input stream that was returned by openImageInputStream() - * */ - public InputStream reopenImageInputStream(InputStream input); - - /** - * Returns a string that identifies the image resource. Example: file - * path of an image - */ - public String getImageLoaderCacheKey(); - } + private static final String TAG = "ImageLoader"; + private static ImageLoader singleton; + + public static final int IMAGE_TYPE_THUMBNAIL = 0; + public static final int IMAGE_TYPE_COVER = 1; + + private Handler handler; + private ExecutorService executor; + + /** + * Stores references to loaded bitmaps. Bitmaps can be accessed by the id of + * the FeedImage the bitmap belongs to. + */ + + final int memClass = ((ActivityManager) PodcastApp.getInstance() + .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); + + // Use 1/8th of the available memory for this memory cache. + final int thumbnailCacheSize = 1024 * 1024 * memClass / 8; + + private LruCache<String, CachedBitmap> coverCache; + private LruCache<String, CachedBitmap> thumbnailCache; + + private ImageLoader() { + handler = new Handler(); + executor = createExecutor(); + + coverCache = new LruCache<String, CachedBitmap>(1); + + thumbnailCache = new LruCache<String, CachedBitmap>(thumbnailCacheSize) { + + @SuppressLint("NewApi") + @Override + protected int sizeOf(String key, CachedBitmap value) { + if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= 12) + return value.getBitmap().getByteCount(); + else + return (value.getBitmap().getRowBytes() * value.getBitmap() + .getHeight()); + + } + + }; + } + + private ExecutorService createExecutor() { + return Executors.newFixedThreadPool(Runtime.getRuntime() + .availableProcessors(), new ThreadFactory() { + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.MIN_PRIORITY); + return t; + } + }); + } + + public static synchronized ImageLoader getInstance() { + if (singleton == null) { + singleton = new ImageLoader(); + } + return singleton; + } + + /** + * Load a bitmap from the cover cache. If the bitmap is not in the cache, it + * will be loaded from the disk. This method should either be called if the + * ImageView's size has already been set or inside a Runnable which is + * posted to the ImageView's message queue. + */ + public void loadCoverBitmap(ImageWorkerTaskResource source, ImageView target) { + loadCoverBitmap(source, target, target.getHeight()); + } + + /** + * Load a bitmap from the cover cache. If the bitmap is not in the cache, it + * will be loaded from the disk. This method should either be called if the + * ImageView's size has already been set or inside a Runnable which is + * posted to the ImageView's message queue. + */ + public void loadCoverBitmap(ImageWorkerTaskResource source, + ImageView target, int length) { + final int defaultCoverResource = getDefaultCoverResource(target + .getContext()); + final String cacheKey; + if (source != null && (cacheKey = source.getImageLoaderCacheKey()) != null) { + final Object currentTag = target.getTag(R.id.imageloader_key); + if (currentTag == null || !cacheKey.equals(currentTag)) { + target.setTag(R.id.imageloader_key, cacheKey); + CachedBitmap cBitmap = getBitmapFromCoverCache(cacheKey); + if (cBitmap != null && cBitmap.getLength() >= length) { + target.setImageBitmap(cBitmap.getBitmap()); + } else { + target.setImageResource(defaultCoverResource); + BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( + handler, target, source, length, IMAGE_TYPE_COVER); + executor.submit(worker); + } + } + } else { + target.setImageResource(defaultCoverResource); + } + } + + /** + * Load a bitmap from the thumbnail cache. If the bitmap is not in the + * cache, it will be loaded from the disk. This method should either be + * called if the ImageView's size has already been set or inside a Runnable + * which is posted to the ImageView's message queue. + */ + public void loadThumbnailBitmap(ImageWorkerTaskResource source, + ImageView target) { + loadThumbnailBitmap(source, target, target.getHeight()); + } + + /** + * Load a bitmap from the thumbnail cache. If the bitmap is not in the + * cache, it will be loaded from the disk. This method should either be + * called if the ImageView's size has already been set or inside a Runnable + * which is posted to the ImageView's message queue. + */ + public void loadThumbnailBitmap(ImageWorkerTaskResource source, + ImageView target, int length) { + final int defaultCoverResource = getDefaultCoverResource(target + .getContext()); + final String cacheKey; + if (source != null && (cacheKey = source.getImageLoaderCacheKey()) != null) { + final Object currentTag = target.getTag(R.id.imageloader_key); + if (currentTag == null || !cacheKey.equals(currentTag)) { + target.setTag(R.id.imageloader_key, cacheKey); + CachedBitmap cBitmap = getBitmapFromThumbnailCache(cacheKey); + if (cBitmap != null && cBitmap.getLength() >= length) { + target.setImageBitmap(cBitmap.getBitmap()); + } else { + target.setImageResource(defaultCoverResource); + BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( + handler, target, source, length, IMAGE_TYPE_THUMBNAIL); + executor.submit(worker); + } + } + } else { + target.setImageResource(defaultCoverResource); + } + } + + public void clearExecutorQueue() { + executor.shutdownNow(); + if (AppConfig.DEBUG) + Log.d(TAG, "Executor was shut down."); + executor = createExecutor(); + + } + + public void wipeImageCache() { + coverCache.evictAll(); + thumbnailCache.evictAll(); + } + + public boolean isInThumbnailCache(String fileUrl) { + return thumbnailCache.get(fileUrl) != null; + } + + private CachedBitmap getBitmapFromThumbnailCache(String key) { + return thumbnailCache.get(key); + } + + public void addBitmapToThumbnailCache(String key, CachedBitmap bitmap) { + thumbnailCache.put(key, bitmap); + } + + public boolean isInCoverCache(String fileUrl) { + return coverCache.get(fileUrl) != null; + } + + private CachedBitmap getBitmapFromCoverCache(String key) { + return coverCache.get(key); + } + + public void addBitmapToCoverCache(String key, CachedBitmap bitmap) { + coverCache.put(key, bitmap); + } + + private int getDefaultCoverResource(Context context) { + return android.R.color.transparent; + } + + /** + * Used by the BitmapDecodeWorker task to retrieve the source of the bitmap. + */ + public interface ImageWorkerTaskResource { + /** + * Opens a new InputStream that can be decoded as a bitmap by the + * BitmapFactory. + */ + public InputStream openImageInputStream(); + + /** + * Returns an InputStream that points to the beginning of the image + * resource. Implementations can either create a new InputStream or + * reset the existing one, depending on their implementation of + * openInputStream. If a new InputStream is returned, the one given as a + * parameter MUST be closed. + * + * @param input The input stream that was returned by openImageInputStream() + */ + public InputStream reopenImageInputStream(InputStream input); + + /** + * Returns a string that identifies the image resource. Example: file + * path of an image + */ + public String getImageLoaderCacheKey(); + } } |