Browse files

Update code style + copyrights

  • Loading branch information...
1 parent 6f62e81 commit b34e45be0b047e20f4e13df94c99ec1a30262892 @chrisbanes committed Feb 19, 2013
View
1,272 library/src/uk/co/senab/bitmapcache/BitmapLruCache.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2013 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,17 +12,10 @@
* 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 uk.co.senab.bitmapcache;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.HashMap;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.ReentrantLock;
+import com.jakewharton.DiskLruCache;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -32,42 +25,44 @@
import android.os.Process;
import android.util.Log;
-import com.jakewharton.DiskLruCache;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
/**
- * A cache which can be set to use multiple layers of caching for Bitmap objects
- * in an Android app. Instances are created via a {@link Builder} instance,
- * which can be used to alter the settings of the resulting cache.
- *
- * <p>
- * Instances of this class should ideally be kept globally with the application,
- * for example in the {@link android.app.Application Application} object. You
- * should also use the bundled {@link CacheableImageView} wherever possible, as
- * the memory cache has a close relationship with it.
- * </p>
- *
- * <p>
- * Clients can call {@link #get(String)} to retrieve a cached value from the
- * given Url. This will check all available caches for the value. There are also
- * the {@link #getFromDiskCache(String, android.graphics.BitmapFactory.Options)} and {@link #getFromMemoryCache(String)}
- * which allow more granular access.
- * </p>
- *
- * <p>
- * There are a number of update methods. {@link #put(String, InputStream)} and
- * {@link #put(String, InputStream)} are the preferred versions of the
- * method, as they allow 1:1 caching to disk of the original content. <br />
- * {@link #put(String, Bitmap)}} should only be used if you can't get access to the original InputStream.
+ * A cache which can be set to use multiple layers of caching for Bitmap objects in an Android app.
+ * Instances are created via a {@link Builder} instance, which can be used to alter the settings of
+ * the resulting cache.
+ *
+ * <p> Instances of this class should ideally be kept globally with the application, for example in
+ * the {@link android.app.Application Application} object. You should also use the bundled {@link
+ * CacheableImageView} wherever possible, as the memory cache has a close relationship with it.
* </p>
- *
+ *
+ * <p> Clients can call {@link #get(String)} to retrieve a cached value from the given Url. This
+ * will check all available caches for the value. There are also the {@link
+ * #getFromDiskCache(String, android.graphics.BitmapFactory.Options)} and {@link
+ * #getFromMemoryCache(String)} which allow more granular access. </p>
+ *
+ * <p> There are a number of update methods. {@link #put(String, InputStream)} and {@link
+ * #put(String, InputStream)} are the preferred versions of the method, as they allow 1:1 caching to
+ * disk of the original content. <br /> {@link #put(String, Bitmap)}} should only be used if you
+ * can't get access to the original InputStream. </p>
+ *
* @author Chris Banes
*/
public class BitmapLruCache {
/**
- * The recycle policy controls if the {@link android.graphics.Bitmap#recycle()} is automatically called, when it is
- * no longer being used. To set this, use the
- * {@link Builder#setRecyclePolicy(uk.co.senab.bitmapcache.BitmapLruCache.RecyclePolicy) Builder.setRecyclePolicy()} method.
+ * The recycle policy controls if the {@link android.graphics.Bitmap#recycle()} is automatically
+ * called, when it is no longer being used. To set this, use the {@link
+ * Builder#setRecyclePolicy(uk.co.senab.bitmapcache.BitmapLruCache.RecyclePolicy)
+ * Builder.setRecyclePolicy()} method.
*/
public static enum RecyclePolicy {
/**
@@ -103,608 +98,575 @@ boolean canRecycle() {
// flushed
static final int DISK_CACHE_FLUSH_DELAY_SECS = 5;
- /**
- * @throws IllegalStateException if the calling thread is the main/UI
- * thread.
- */
- private static void checkNotOnMainThread() {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- throw new IllegalStateException("This method should not be called from the main/UI thread.");
- }
- }
-
- /**
- * The disk cache only accepts a reduced range of characters for the key
- * values. This method transforms the {@code url} into something accepted
- * from {@link DiskLruCache}. Currently we simply return a MD5 hash of the
- * url.
- *
- * @param url - Key to be transformed
- * @return key which can be used for the disk cache
- */
- private static String transformUrlForDiskCacheKey(String url) {
- return Md5.encode(url);
- }
-
- private DiskLruCache mDiskCache;
- private final BitmapMemoryLruCache mMemoryCache;
+ /**
+ * @throws IllegalStateException if the calling thread is the main/UI thread.
+ */
+ private static void checkNotOnMainThread() {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new IllegalStateException(
+ "This method should not be called from the main/UI thread.");
+ }
+ }
+
+ /**
+ * The disk cache only accepts a reduced range of characters for the key values. This method
+ * transforms the {@code url} into something accepted from {@link DiskLruCache}. Currently we
+ * simply return a MD5 hash of the url.
+ *
+ * @param url - Key to be transformed
+ * @return key which can be used for the disk cache
+ */
+ private static String transformUrlForDiskCacheKey(String url) {
+ return Md5.encode(url);
+ }
+
+ private DiskLruCache mDiskCache;
+
+ private final BitmapMemoryLruCache mMemoryCache;
private final RecyclePolicy mRecyclePolicy;
- // Variables which are only used when the Disk Cache is enabled
- private HashMap<String, ReentrantLock> mDiskCacheEditLocks;
- private ScheduledThreadPoolExecutor mDiskCacheFlusherExecutor;
- private DiskCacheFlushRunnable mDiskCacheFlusherRunnable;
+ // Variables which are only used when the Disk Cache is enabled
+ private HashMap<String, ReentrantLock> mDiskCacheEditLocks;
- // Transient
- private ScheduledFuture<?> mDiskCacheFuture;
+ private ScheduledThreadPoolExecutor mDiskCacheFlusherExecutor;
- protected BitmapLruCache(BitmapMemoryLruCache memoryCache, RecyclePolicy recyclePolicy) {
- mMemoryCache = memoryCache;
+ private DiskCacheFlushRunnable mDiskCacheFlusherRunnable;
+
+ // Transient
+ private ScheduledFuture<?> mDiskCacheFuture;
+
+ protected BitmapLruCache(BitmapMemoryLruCache memoryCache, RecyclePolicy recyclePolicy) {
+ mMemoryCache = memoryCache;
mRecyclePolicy = recyclePolicy;
- }
-
- /**
- * Returns whether any of the enabled caches contain the specified URL.
- * <p/>
- * If you have the disk cache enabled, you should not call this method from
- * main/UI thread.
- *
- * @param url the URL to search for.
- * @return {@code true} if any of the caches contain the specified URL,
- * {@code false} otherwise.
- */
- public boolean contains(String url) {
- return containsInMemoryCache(url) || containsInDiskCache(url);
- }
-
- /**
- * Returns whether the Disk Cache contains the specified URL. You should not
- * call this method from main/UI thread.
- *
- * @param url the URL to search for.
- * @return {@code true} if the Disk Cache is enabled and contains the
- * specified URL, {@code false} otherwise.
- */
- public boolean containsInDiskCache(String url) {
- if (null != mDiskCache) {
- checkNotOnMainThread();
-
- try {
- return null != mDiskCache.get(url);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- return false;
- }
-
- /**
- * Returns whether the Memory Cache contains the specified URL. This method
- * is safe to be called from the main thread.
- *
- * @param url the URL to search for.
- * @return {@code true} if the Memory Cache is enabled and contains the
- * specified URL, {@code false} otherwise.
- */
- public boolean containsInMemoryCache(String url) {
- return null != mMemoryCache && null != mMemoryCache.get(url);
- }
-
- /**
- * Returns the value for {@code url}. This will check all caches currently
- * enabled.
- * <p/>
- * If you have the disk cache enabled, you should not call this method from
- * main/UI thread.
- *
- * @param url - String representing the URL of the image
- */
- public CacheableBitmapDrawable get(String url) {
- return get(url, null);
- }
-
- /**
- * Returns the value for {@code url}. This will check all caches currently
- * enabled.
- * <p/>
- * If you have the disk cache enabled, you should not call this method from
- * main/UI thread.
- *
- * @param url - String representing the URL of the image
- * @param decodeOpts - Options used for decoding the contents from the disk
- * cache only.
- */
- public CacheableBitmapDrawable get(String url, BitmapFactory.Options decodeOpts) {
- CacheableBitmapDrawable result;
-
- // First try Memory Cache
- result = getFromMemoryCache(url);
-
- if (null == result) {
- // Memory Cache failed, so try Disk Cache
- result = getFromDiskCache(url, decodeOpts);
- }
-
- return result;
- }
-
- /**
- * Returns the value for {@code url} in the disk cache only. You should not
- * call this method from main/UI thread.
- * <p/>
- * If enabled, the result of this method will be cached in the memory cache.
- * <p />
- * Unless you have a specific requirement to only query the disk cache, you
- * should call {@link #get(String)} instead.
- *
- * @param url - String representing the URL of the image
- * @param decodeOpts - Options used for decoding the contents from the disk
- * cache.
- * @return Value for {@code url} from disk cache, or {@code null} if the
- * disk cache is not enabled.
- */
- public CacheableBitmapDrawable getFromDiskCache(final String url, final BitmapFactory.Options decodeOpts) {
- CacheableBitmapDrawable result = null;
-
- if (null != mDiskCache) {
- checkNotOnMainThread();
-
- try {
- final String key = transformUrlForDiskCacheKey(url);
- DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
- if (null != snapshot) {
- // Try and decode bitmap
- Bitmap bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0), null, decodeOpts);
-
- if (null != bitmap) {
- result = new CacheableBitmapDrawable(url, bitmap, mRecyclePolicy);
+ }
+
+ /**
+ * Returns whether any of the enabled caches contain the specified URL. <p/> If you have the
+ * disk cache enabled, you should not call this method from main/UI thread.
+ *
+ * @param url the URL to search for.
+ * @return {@code true} if any of the caches contain the specified URL, {@code false}
+ * otherwise.
+ */
+ public boolean contains(String url) {
+ return containsInMemoryCache(url) || containsInDiskCache(url);
+ }
+
+ /**
+ * Returns whether the Disk Cache contains the specified URL. You should not call this method
+ * from main/UI thread.
+ *
+ * @param url the URL to search for.
+ * @return {@code true} if the Disk Cache is enabled and contains the specified URL, {@code
+ * false} otherwise.
+ */
+ public boolean containsInDiskCache(String url) {
+ if (null != mDiskCache) {
+ checkNotOnMainThread();
+
+ try {
+ return null != mDiskCache.get(url);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the Memory Cache contains the specified URL. This method is safe to be called
+ * from the main thread.
+ *
+ * @param url the URL to search for.
+ * @return {@code true} if the Memory Cache is enabled and contains the specified URL, {@code
+ * false} otherwise.
+ */
+ public boolean containsInMemoryCache(String url) {
+ return null != mMemoryCache && null != mMemoryCache.get(url);
+ }
+
+ /**
+ * Returns the value for {@code url}. This will check all caches currently enabled. <p/> If you
+ * have the disk cache enabled, you should not call this method from main/UI thread.
+ *
+ * @param url - String representing the URL of the image
+ */
+ public CacheableBitmapDrawable get(String url) {
+ return get(url, null);
+ }
+
+ /**
+ * Returns the value for {@code url}. This will check all caches currently enabled. <p/> If you
+ * have the disk cache enabled, you should not call this method from main/UI thread.
+ *
+ * @param url - String representing the URL of the image
+ * @param decodeOpts - Options used for decoding the contents from the disk cache only.
+ */
+ public CacheableBitmapDrawable get(String url, BitmapFactory.Options decodeOpts) {
+ CacheableBitmapDrawable result;
+
+ // First try Memory Cache
+ result = getFromMemoryCache(url);
+
+ if (null == result) {
+ // Memory Cache failed, so try Disk Cache
+ result = getFromDiskCache(url, decodeOpts);
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the value for {@code url} in the disk cache only. You should not call this method
+ * from main/UI thread. <p/> If enabled, the result of this method will be cached in the memory
+ * cache. <p /> Unless you have a specific requirement to only query the disk cache, you should
+ * call {@link #get(String)} instead.
+ *
+ * @param url - String representing the URL of the image
+ * @param decodeOpts - Options used for decoding the contents from the disk cache.
+ * @return Value for {@code url} from disk cache, or {@code null} if the disk cache is not
+ * enabled.
+ */
+ public CacheableBitmapDrawable getFromDiskCache(final String url,
+ final BitmapFactory.Options decodeOpts) {
+ CacheableBitmapDrawable result = null;
+
+ if (null != mDiskCache) {
+ checkNotOnMainThread();
+
+ try {
+ final String key = transformUrlForDiskCacheKey(url);
+ DiskLruCache.Snapshot snapshot = mDiskCache.get(key);
+ if (null != snapshot) {
+ // Try and decode bitmap
+ Bitmap bitmap = BitmapFactory
+ .decodeStream(snapshot.getInputStream(0), null, decodeOpts);
+
+ if (null != bitmap) {
+ result = new CacheableBitmapDrawable(url, bitmap, mRecyclePolicy);
if (null != mMemoryCache) {
mMemoryCache.put(result);
}
} else {
- // If we get here, the file in the cache can't be
- // decoded. Remove it and schedule a flush.
- mDiskCache.remove(key);
- scheduleDiskCacheFlush();
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- return result;
- }
-
- /**
- * Returns the value for {@code url} in the memory cache only. This method
- * is safe to be called from the main thread.
- * <p />
- * You should check the result of this method before starting a threaded
- * call.
- *
- * @param url - String representing the URL of the image
- * @return Value for {@code url} from memory cache, or {@code null} if the
- * disk cache is not enabled.
- */
- public CacheableBitmapDrawable getFromMemoryCache(final String url) {
- CacheableBitmapDrawable result = null;
-
- if (null != mMemoryCache) {
- synchronized (mMemoryCache) {
- result = mMemoryCache.get(url);
-
- // If we get a value, but it has a invalid bitmap, remove it
- if (null != result && !result.hasValidBitmap()) {
- mMemoryCache.remove(url);
- result = null;
- }
- }
- }
-
- return result;
- }
-
- /**
- * Caches {@code bitmap} for {@code url} into all enabled caches. If the
- * disk cache is enabled, the bitmap will be compressed losslessly.
- * <p/>
- * If you have the disk cache enabled, you should not call this method from
- * main/UI thread.
- *
- * @param url - String representing the URL of the image.
- * @param bitmap - Bitmap which has been decoded from {@code url}.
- * @return CacheableBitmapDrawable which can be used to display the bitmap.
- */
- public CacheableBitmapDrawable put(final String url, final Bitmap bitmap) {
- CacheableBitmapDrawable d = new CacheableBitmapDrawable(url, bitmap, mRecyclePolicy);
-
- if (null != mMemoryCache) {
- mMemoryCache.put(d);
- }
-
- if (null != mDiskCache) {
- checkNotOnMainThread();
-
- final ReentrantLock lock = getLockForDiskCacheEdit(url);
- lock.lock();
- try {
- DiskLruCache.Editor editor = mDiskCache.edit(transformUrlForDiskCacheKey(url));
- Util.saveBitmap(bitmap, editor.newOutputStream(0));
- editor.commit();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- scheduleDiskCacheFlush();
- }
- }
-
- return d;
- }
-
- /**
- * Caches resulting bitmap from {@code inputStream} for {@code url} into all
- * enabled caches. This version of the method should be preferred as it
- * allows the original image contents to be cached, rather than a
- * re-compressed version.
- * <p />
- * The contents of the InputStream will be copied to a temporary file, then
- * the file will be decoded into a Bitmap. Providing the decode worked:
- * <ul>
- * <li>If the memory cache is enabled, the decoded Bitmap will be cached to
- * memory.</li>
- * <li>If the disk cache is enabled, the contents of the original stream
- * will be cached to disk.</li>
- * </ul>
- * <p/>
- * You should not call this method from the main/UI thread.
- *
- * @param url - String representing the URL of the image
- * @param inputStream - InputStream opened from {@code url}
- * @return CacheableBitmapDrawable which can be used to display the bitmap.
- */
- public CacheableBitmapDrawable put(final String url, final InputStream inputStream) {
- return put(url, inputStream, null);
- }
-
- /**
- * Caches resulting bitmap from {@code inputStream} for {@code url} into all
- * enabled caches. This version of the method should be preferred as it
- * allows the original image contents to be cached, rather than a
- * re-compressed version.
- * <p />
- * The contents of the InputStream will be copied to a temporary file, then
- * the file will be decoded into a Bitmap, using the optional
- * <code>decodeOpts</code>. Providing the decode worked:
- * <ul>
- * <li>If the memory cache is enabled, the decoded Bitmap will be cached to
- * memory.</li>
- * <li>If the disk cache is enabled, the contents of the original stream
- * will be cached to disk.</li>
- * </ul>
- * <p/>
- * You should not call this method from the main/UI thread.
- *
- * @param url - String representing the URL of the image
- * @param inputStream - InputStream opened from {@code url}
- * @param decodeOpts - Options used for decoding. This does not affect what
- * is cached in the disk cache (if enabled).
- * @return CacheableBitmapDrawable which can be used to display the bitmap.
- */
- public CacheableBitmapDrawable put(final String url, final InputStream inputStream,
- final BitmapFactory.Options decodeOpts) {
- checkNotOnMainThread();
-
- // First we need to save the stream contents to a temporary file, so it
- // can be read multiple times
- File tmpFile = null;
- try {
- tmpFile = File.createTempFile("bitmapcache_", null);
-
- // Pipe InputStream to file
- Util.copy(inputStream, tmpFile);
-
- try {
- // Close the original InputStream
- inputStream.close();
- } catch (IOException e) {
- // NO-OP - Ignore
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
-
- CacheableBitmapDrawable d = null;
-
- if (null != tmpFile) {
- // Try and decode File
- Bitmap bitmap = BitmapFactory.decodeFile(tmpFile.getAbsolutePath(), decodeOpts);
-
- if (null != bitmap) {
- d = new CacheableBitmapDrawable(url, bitmap, mRecyclePolicy);
-
- if (null != mMemoryCache) {
- d.setCached(true);
- mMemoryCache.put(d.getUrl(), d);
- }
-
- if (null != mDiskCache) {
- final ReentrantLock lock = getLockForDiskCacheEdit(url);
- lock.lock();
- try {
- DiskLruCache.Editor editor = mDiskCache.edit(transformUrlForDiskCacheKey(url));
- Util.copy(tmpFile, editor.newOutputStream(0));
- editor.commit();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- scheduleDiskCacheFlush();
- }
- }
- }
-
- // Finally, delete the temporary file
- tmpFile.delete();
- }
-
- return d;
- }
-
- /**
- * Removes the entry for {@code url} from all enabled caches, if it exists.
- * <p/>
- * If you have the disk cache enabled, you should not call this method from
- * main/UI thread.
- */
- public void remove(String url) {
- if (null != mMemoryCache) {
- mMemoryCache.remove(url);
- }
-
- if (null != mDiskCache) {
- checkNotOnMainThread();
-
- try {
- mDiskCache.remove(transformUrlForDiskCacheKey(url));
- scheduleDiskCacheFlush();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * This method iterates through the memory cache (if enabled) and removes
- * any entries which are not currently being displayed. A good place to call
- * this would be from {@link android.app.Application#onLowMemory()
- * Application.onLowMemory()}.
- */
- public void trimMemory() {
- if (null != mMemoryCache) {
- mMemoryCache.trimMemory();
- }
- }
-
- synchronized void setDiskCache(DiskLruCache diskCache) {
- mDiskCache = diskCache;
-
- if (null != diskCache) {
- mDiskCacheEditLocks = new HashMap<String, ReentrantLock>();
- mDiskCacheFlusherExecutor = new ScheduledThreadPoolExecutor(1);
- mDiskCacheFlusherRunnable = new DiskCacheFlushRunnable(diskCache);
- }
- }
-
- private ReentrantLock getLockForDiskCacheEdit(String url) {
- synchronized (mDiskCacheEditLocks) {
- ReentrantLock lock = mDiskCacheEditLocks.get(url);
- if (null == lock) {
- lock = new ReentrantLock();
- mDiskCacheEditLocks.put(url, lock);
- }
- return lock;
- }
- }
-
- private void scheduleDiskCacheFlush() {
- // If we already have a flush scheduled, cancel it
- if (null != mDiskCacheFuture) {
- mDiskCacheFuture.cancel(false);
- }
-
- // Schedule a flush
- mDiskCacheFuture = mDiskCacheFlusherExecutor.schedule(mDiskCacheFlusherRunnable, DISK_CACHE_FLUSH_DELAY_SECS,
- TimeUnit.SECONDS);
- }
-
- /**
- * Builder class for {link {@link BitmapLruCache}. An example call:
- *
- * <pre>
- * BitmapLruCache.Builder builder = new BitmapLruCache.Builder();
- * builder.setMemoryCacheEnabled(true).setMemoryCacheMaxSizeUsingHeapSize(this);
- * builder.setDiskCacheEnabled(true).setDiskCacheLocation(...);
- *
- * BitmapLruCache cache = builder.build();
- * </pre>
- *
- * @author Chris Banes
- */
- public final static class Builder {
-
- static final int MEGABYTE = 1024 * 1024;
-
- static final float DEFAULT_MEMORY_CACHE_HEAP_RATIO = 1f / 8f;
- static final float MAX_MEMORY_CACHE_HEAP_RATIO = 0.75f;
-
- static final int DEFAULT_DISK_CACHE_MAX_SIZE_MB = 10;
- static final int DEFAULT_MEM_CACHE_MAX_SIZE_MB = 3;
+ // If we get here, the file in the cache can't be
+ // decoded. Remove it and schedule a flush.
+ mDiskCache.remove(key);
+ scheduleDiskCacheFlush();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns the value for {@code url} in the memory cache only. This method is safe to be called
+ * from the main thread. <p /> You should check the result of this method before starting a
+ * threaded call.
+ *
+ * @param url - String representing the URL of the image
+ * @return Value for {@code url} from memory cache, or {@code null} if the disk cache is not
+ * enabled.
+ */
+ public CacheableBitmapDrawable getFromMemoryCache(final String url) {
+ CacheableBitmapDrawable result = null;
+
+ if (null != mMemoryCache) {
+ synchronized (mMemoryCache) {
+ result = mMemoryCache.get(url);
+
+ // If we get a value, but it has a invalid bitmap, remove it
+ if (null != result && !result.hasValidBitmap()) {
+ mMemoryCache.remove(url);
+ result = null;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Caches {@code bitmap} for {@code url} into all enabled caches. If the disk cache is enabled,
+ * the bitmap will be compressed losslessly. <p/> If you have the disk cache enabled, you should
+ * not call this method from main/UI thread.
+ *
+ * @param url - String representing the URL of the image.
+ * @param bitmap - Bitmap which has been decoded from {@code url}.
+ * @return CacheableBitmapDrawable which can be used to display the bitmap.
+ */
+ public CacheableBitmapDrawable put(final String url, final Bitmap bitmap) {
+ CacheableBitmapDrawable d = new CacheableBitmapDrawable(url, bitmap, mRecyclePolicy);
+
+ if (null != mMemoryCache) {
+ mMemoryCache.put(d);
+ }
+
+ if (null != mDiskCache) {
+ checkNotOnMainThread();
+
+ final ReentrantLock lock = getLockForDiskCacheEdit(url);
+ lock.lock();
+ try {
+ DiskLruCache.Editor editor = mDiskCache.edit(transformUrlForDiskCacheKey(url));
+ Util.saveBitmap(bitmap, editor.newOutputStream(0));
+ editor.commit();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ lock.unlock();
+ scheduleDiskCacheFlush();
+ }
+ }
+
+ return d;
+ }
+
+ /**
+ * Caches resulting bitmap from {@code inputStream} for {@code url} into all enabled caches.
+ * This version of the method should be preferred as it allows the original image contents to be
+ * cached, rather than a re-compressed version. <p /> The contents of the InputStream will be
+ * copied to a temporary file, then the file will be decoded into a Bitmap. Providing the decode
+ * worked: <ul> <li>If the memory cache is enabled, the decoded Bitmap will be cached to
+ * memory.</li> <li>If the disk cache is enabled, the contents of the original stream will be
+ * cached to disk.</li> </ul> <p/> You should not call this method from the main/UI thread.
+ *
+ * @param url - String representing the URL of the image
+ * @param inputStream - InputStream opened from {@code url}
+ * @return CacheableBitmapDrawable which can be used to display the bitmap.
+ */
+ public CacheableBitmapDrawable put(final String url, final InputStream inputStream) {
+ return put(url, inputStream, null);
+ }
+
+ /**
+ * Caches resulting bitmap from {@code inputStream} for {@code url} into all enabled caches.
+ * This version of the method should be preferred as it allows the original image contents to be
+ * cached, rather than a re-compressed version. <p /> The contents of the InputStream will be
+ * copied to a temporary file, then the file will be decoded into a Bitmap, using the optional
+ * <code>decodeOpts</code>. Providing the decode worked: <ul> <li>If the memory cache is
+ * enabled, the decoded Bitmap will be cached to memory.</li> <li>If the disk cache is enabled,
+ * the contents of the original stream will be cached to disk.</li> </ul> <p/> You should not
+ * call this method from the main/UI thread.
+ *
+ * @param url - String representing the URL of the image
+ * @param inputStream - InputStream opened from {@code url}
+ * @param decodeOpts - Options used for decoding. This does not affect what is cached in the
+ * disk cache (if enabled).
+ * @return CacheableBitmapDrawable which can be used to display the bitmap.
+ */
+ public CacheableBitmapDrawable put(final String url, final InputStream inputStream,
+ final BitmapFactory.Options decodeOpts) {
+ checkNotOnMainThread();
+
+ // First we need to save the stream contents to a temporary file, so it
+ // can be read multiple times
+ File tmpFile = null;
+ try {
+ tmpFile = File.createTempFile("bitmapcache_", null);
+
+ // Pipe InputStream to file
+ Util.copy(inputStream, tmpFile);
+
+ try {
+ // Close the original InputStream
+ inputStream.close();
+ } catch (IOException e) {
+ // NO-OP - Ignore
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ CacheableBitmapDrawable d = null;
+
+ if (null != tmpFile) {
+ // Try and decode File
+ Bitmap bitmap = BitmapFactory.decodeFile(tmpFile.getAbsolutePath(), decodeOpts);
+
+ if (null != bitmap) {
+ d = new CacheableBitmapDrawable(url, bitmap, mRecyclePolicy);
+
+ if (null != mMemoryCache) {
+ d.setCached(true);
+ mMemoryCache.put(d.getUrl(), d);
+ }
+
+ if (null != mDiskCache) {
+ final ReentrantLock lock = getLockForDiskCacheEdit(url);
+ lock.lock();
+ try {
+ DiskLruCache.Editor editor = mDiskCache
+ .edit(transformUrlForDiskCacheKey(url));
+ Util.copy(tmpFile, editor.newOutputStream(0));
+ editor.commit();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ lock.unlock();
+ scheduleDiskCacheFlush();
+ }
+ }
+ }
+
+ // Finally, delete the temporary file
+ tmpFile.delete();
+ }
+
+ return d;
+ }
+
+ /**
+ * Removes the entry for {@code url} from all enabled caches, if it exists. <p/> If you have the
+ * disk cache enabled, you should not call this method from main/UI thread.
+ */
+ public void remove(String url) {
+ if (null != mMemoryCache) {
+ mMemoryCache.remove(url);
+ }
+
+ if (null != mDiskCache) {
+ checkNotOnMainThread();
+
+ try {
+ mDiskCache.remove(transformUrlForDiskCacheKey(url));
+ scheduleDiskCacheFlush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * This method iterates through the memory cache (if enabled) and removes any entries which are
+ * not currently being displayed. A good place to call this would be from {@link
+ * android.app.Application#onLowMemory() Application.onLowMemory()}.
+ */
+ public void trimMemory() {
+ if (null != mMemoryCache) {
+ mMemoryCache.trimMemory();
+ }
+ }
+
+ synchronized void setDiskCache(DiskLruCache diskCache) {
+ mDiskCache = diskCache;
+
+ if (null != diskCache) {
+ mDiskCacheEditLocks = new HashMap<String, ReentrantLock>();
+ mDiskCacheFlusherExecutor = new ScheduledThreadPoolExecutor(1);
+ mDiskCacheFlusherRunnable = new DiskCacheFlushRunnable(diskCache);
+ }
+ }
+
+ private ReentrantLock getLockForDiskCacheEdit(String url) {
+ synchronized (mDiskCacheEditLocks) {
+ ReentrantLock lock = mDiskCacheEditLocks.get(url);
+ if (null == lock) {
+ lock = new ReentrantLock();
+ mDiskCacheEditLocks.put(url, lock);
+ }
+ return lock;
+ }
+ }
+
+ private void scheduleDiskCacheFlush() {
+ // If we already have a flush scheduled, cancel it
+ if (null != mDiskCacheFuture) {
+ mDiskCacheFuture.cancel(false);
+ }
+
+ // Schedule a flush
+ mDiskCacheFuture = mDiskCacheFlusherExecutor
+ .schedule(mDiskCacheFlusherRunnable, DISK_CACHE_FLUSH_DELAY_SECS,
+ TimeUnit.SECONDS);
+ }
+
+ /**
+ * Builder class for {link {@link BitmapLruCache}. An example call:
+ *
+ * <pre>
+ * BitmapLruCache.Builder builder = new BitmapLruCache.Builder();
+ * builder.setMemoryCacheEnabled(true).setMemoryCacheMaxSizeUsingHeapSize(this);
+ * builder.setDiskCacheEnabled(true).setDiskCacheLocation(...);
+ *
+ * BitmapLruCache cache = builder.build();
+ * </pre>
+ *
+ * @author Chris Banes
+ */
+ public final static class Builder {
+
+ static final int MEGABYTE = 1024 * 1024;
+
+ static final float DEFAULT_MEMORY_CACHE_HEAP_RATIO = 1f / 8f;
+
+ static final float MAX_MEMORY_CACHE_HEAP_RATIO = 0.75f;
+
+ static final int DEFAULT_DISK_CACHE_MAX_SIZE_MB = 10;
+
+ static final int DEFAULT_MEM_CACHE_MAX_SIZE_MB = 3;
static final RecyclePolicy DEFAULT_RECYCLE_POLICY = RecyclePolicy.ALWAYS;
- // Only used for Javadoc
- static final float DEFAULT_MEMORY_CACHE_HEAP_PERCENTAGE = DEFAULT_MEMORY_CACHE_HEAP_RATIO * 100;
- static final float MAX_MEMORY_CACHE_HEAP_PERCENTAGE = MAX_MEMORY_CACHE_HEAP_RATIO * 100;
+ // Only used for Javadoc
+ static final float DEFAULT_MEMORY_CACHE_HEAP_PERCENTAGE = DEFAULT_MEMORY_CACHE_HEAP_RATIO
+ * 100;
+
+ static final float MAX_MEMORY_CACHE_HEAP_PERCENTAGE = MAX_MEMORY_CACHE_HEAP_RATIO * 100;
+
+ private static long getHeapSize() {
+ return Runtime.getRuntime().maxMemory();
+ }
+
+ private boolean mDiskCacheEnabled;
- private static long getHeapSize() {
- return Runtime.getRuntime().maxMemory();
- }
+ private File mDiskCacheLocation;
- private boolean mDiskCacheEnabled;
- private File mDiskCacheLocation;
- private long mDiskCacheMaxSize;
+ private long mDiskCacheMaxSize;
+
+ private boolean mMemoryCacheEnabled;
+
+ private int mMemoryCacheMaxSize;
- private boolean mMemoryCacheEnabled;
- private int mMemoryCacheMaxSize;
private RecyclePolicy mRecyclePolicy;
- public Builder() {
- // Disk Cache is disabled by default, but it's default size is set
- mDiskCacheMaxSize = DEFAULT_DISK_CACHE_MAX_SIZE_MB * MEGABYTE;
+ public Builder() {
+ // Disk Cache is disabled by default, but it's default size is set
+ mDiskCacheMaxSize = DEFAULT_DISK_CACHE_MAX_SIZE_MB * MEGABYTE;
- // Memory Cache is enabled by default, with a small maximum size
- mMemoryCacheEnabled = true;
- mMemoryCacheMaxSize = DEFAULT_MEM_CACHE_MAX_SIZE_MB * MEGABYTE;
+ // Memory Cache is enabled by default, with a small maximum size
+ mMemoryCacheEnabled = true;
+ mMemoryCacheMaxSize = DEFAULT_MEM_CACHE_MAX_SIZE_MB * MEGABYTE;
mRecyclePolicy = DEFAULT_RECYCLE_POLICY;
- }
-
- /**
- * @return A new {@link BitmapLruCache} created with the arguments
- * supplied to this builder.
- */
- public BitmapLruCache build() {
- BitmapMemoryLruCache memoryCache = null;
-
- if (isValidOptionsForMemoryCache()) {
- if (Constants.DEBUG) {
- Log.d("BitmapLruCache.Builder", "Creating Memory Cache");
- }
- memoryCache = new BitmapMemoryLruCache(mMemoryCacheMaxSize);
- }
-
- final BitmapLruCache cache = new BitmapLruCache(memoryCache, mRecyclePolicy);
-
- if (isValidOptionsForDiskCache()) {
- new AsyncTask<Void, Void, DiskLruCache>() {
-
- @Override
- protected DiskLruCache doInBackground(Void... params) {
- try {
- return DiskLruCache.open(mDiskCacheLocation, 0, 1, mDiskCacheMaxSize);
- } catch (IOException e) {
- e.printStackTrace();
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(DiskLruCache result) {
- cache.setDiskCache(result);
- }
-
- }.execute();
- }
-
- return cache;
- }
-
- /**
- * Set whether the Disk Cache should be enabled. Defaults to
- * {@code false}.
- *
- * @return This Builder object to allow for chaining of calls to set
- * methods.
- */
- public Builder setDiskCacheEnabled(boolean enabled) {
- mDiskCacheEnabled = enabled;
- return this;
- }
-
- /**
- * Set the Disk Cache location. This location should be read-writeable.
- *
- * @return This Builder object to allow for chaining of calls to set
- * methods.
- */
- public Builder setDiskCacheLocation(File location) {
- mDiskCacheLocation = location;
- return this;
- }
-
- /**
- * Set the maximum number of bytes the Disk Cache should use to store
- * values. Defaults to {@value #DEFAULT_DISK_CACHE_MAX_SIZE_MB}MB.
- *
- * @return This Builder object to allow for chaining of calls to set
- * methods.
- */
- public Builder setDiskCacheMaxSize(long maxSize) {
- mDiskCacheMaxSize = maxSize;
- return this;
- }
-
- /**
- * Set whether the Memory Cache should be enabled. Defaults to
- * {@code true}.
- *
- * @return This Builder object to allow for chaining of calls to set
- * methods.
- */
- public Builder setMemoryCacheEnabled(boolean enabled) {
- mMemoryCacheEnabled = enabled;
- return this;
- }
-
- /**
- * Set the maximum number of bytes the Memory Cache should use to store
- * values. Defaults to {@value #DEFAULT_MEM_CACHE_MAX_SIZE_MB}MB.
- *
- * @return This Builder object to allow for chaining of calls to set
- * methods.
- */
- public Builder setMemoryCacheMaxSize(int size) {
- mMemoryCacheMaxSize = size;
- return this;
- }
-
- /**
- * Sets the Memory Cache maximum size to be the default value of
- * {@value #DEFAULT_MEMORY_CACHE_HEAP_PERCENTAGE}% of heap size.
+ }
+
+ /**
+ * @return A new {@link BitmapLruCache} created with the arguments supplied to this
+ * builder.
+ */
+ public BitmapLruCache build() {
+ BitmapMemoryLruCache memoryCache = null;
+
+ if (isValidOptionsForMemoryCache()) {
+ if (Constants.DEBUG) {
+ Log.d("BitmapLruCache.Builder", "Creating Memory Cache");
+ }
+ memoryCache = new BitmapMemoryLruCache(mMemoryCacheMaxSize);
+ }
+
+ final BitmapLruCache cache = new BitmapLruCache(memoryCache, mRecyclePolicy);
+
+ if (isValidOptionsForDiskCache()) {
+ new AsyncTask<Void, Void, DiskLruCache>() {
+
+ @Override
+ protected DiskLruCache doInBackground(Void... params) {
+ try {
+ return DiskLruCache.open(mDiskCacheLocation, 0, 1, mDiskCacheMaxSize);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(DiskLruCache result) {
+ cache.setDiskCache(result);
+ }
+
+ }.execute();
+ }
+
+ return cache;
+ }
+
+ /**
+ * Set whether the Disk Cache should be enabled. Defaults to {@code false}.
*
- * @return This Builder object to allow for chaining of calls to set
- * methods.
- */
- public Builder setMemoryCacheMaxSizeUsingHeapSize() {
- return setMemoryCacheMaxSizeUsingHeapSize(DEFAULT_MEMORY_CACHE_HEAP_RATIO);
- }
-
- /**
- * Sets the Memory Cache maximum size to be the given percentage of heap
- * size. This is capped at {@value #MAX_MEMORY_CACHE_HEAP_PERCENTAGE}%
- * of the app heap size.
- *
- * @param percentageOfHeap - percentage of heap size. Valid values are
- * 0.0 <= x <= {@value #MAX_MEMORY_CACHE_HEAP_RATIO}.
+ * @return This Builder object to allow for chaining of calls to set methods.
+ */
+ public Builder setDiskCacheEnabled(boolean enabled) {
+ mDiskCacheEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Set the Disk Cache location. This location should be read-writeable.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods.
+ */
+ public Builder setDiskCacheLocation(File location) {
+ mDiskCacheLocation = location;
+ return this;
+ }
+
+ /**
+ * Set the maximum number of bytes the Disk Cache should use to store values. Defaults to
+ * {@value #DEFAULT_DISK_CACHE_MAX_SIZE_MB}MB.
*
- * @return This Builder object to allow for chaining of calls to set
- * methods.
- */
- public Builder setMemoryCacheMaxSizeUsingHeapSize(float percentageOfHeap) {
- int size = Math.round(getHeapSize() * Math.min(percentageOfHeap, MAX_MEMORY_CACHE_HEAP_RATIO));
- return setMemoryCacheMaxSize(size);
- }
+ * @return This Builder object to allow for chaining of calls to set methods.
+ */
+ public Builder setDiskCacheMaxSize(long maxSize) {
+ mDiskCacheMaxSize = maxSize;
+ return this;
+ }
/**
- * Sets the recycle policy. This controls if {@link android.graphics.Bitmap#recycle()}
- * is called.
+ * Set whether the Memory Cache should be enabled. Defaults to {@code true}.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods.
+ */
+ public Builder setMemoryCacheEnabled(boolean enabled) {
+ mMemoryCacheEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * Set the maximum number of bytes the Memory Cache should use to store values. Defaults to
+ * {@value #DEFAULT_MEM_CACHE_MAX_SIZE_MB}MB.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods.
+ */
+ public Builder setMemoryCacheMaxSize(int size) {
+ mMemoryCacheMaxSize = size;
+ return this;
+ }
+
+ /**
+ * Sets the Memory Cache maximum size to be the default value of {@value
+ * #DEFAULT_MEMORY_CACHE_HEAP_PERCENTAGE}% of heap size.
+ *
+ * @return This Builder object to allow for chaining of calls to set methods.
+ */
+ public Builder setMemoryCacheMaxSizeUsingHeapSize() {
+ return setMemoryCacheMaxSizeUsingHeapSize(DEFAULT_MEMORY_CACHE_HEAP_RATIO);
+ }
+
+ /**
+ * Sets the Memory Cache maximum size to be the given percentage of heap size. This is
+ * capped at {@value #MAX_MEMORY_CACHE_HEAP_PERCENTAGE}% of the app heap size.
+ *
+ * @param percentageOfHeap - percentage of heap size. Valid values are 0.0 <= x <= {@value
+ * #MAX_MEMORY_CACHE_HEAP_RATIO}.
+ * @return This Builder object to allow for chaining of calls to set methods.
+ */
+ public Builder setMemoryCacheMaxSizeUsingHeapSize(float percentageOfHeap) {
+ int size = Math
+ .round(getHeapSize() * Math.min(percentageOfHeap, MAX_MEMORY_CACHE_HEAP_RATIO));
+ return setMemoryCacheMaxSize(size);
+ }
+
+ /**
+ * Sets the recycle policy. This controls if {@link android.graphics.Bitmap#recycle()} is
+ * called.
*
* @param recyclePolicy - New recycle policy, can not be null.
- * @return This Builder object to allow for chaining of calls to set
- * methods.
+ * @return This Builder object to allow for chaining of calls to set methods.
*/
public Builder setRecyclePolicy(RecyclePolicy recyclePolicy) {
if (null == recyclePolicy) {
@@ -715,45 +677,45 @@ public Builder setRecyclePolicy(RecyclePolicy recyclePolicy) {
return this;
}
- private boolean isValidOptionsForDiskCache() {
- if (mDiskCacheEnabled) {
- if (null == mDiskCacheLocation) {
- Log.i(Constants.LOG_TAG,
- "Disk Cache has been enabled, but no location given. Please call setDiskCacheLocation(...)");
- return false;
- }
-
- return true;
- }
- return false;
-
- }
-
- private boolean isValidOptionsForMemoryCache() {
- return mMemoryCacheEnabled && mMemoryCacheMaxSize > 0;
- }
- }
-
- static final class DiskCacheFlushRunnable implements Runnable {
-
- private final DiskLruCache mDiskCache;
-
- public DiskCacheFlushRunnable(DiskLruCache cache) {
- mDiskCache = cache;
- }
-
- public void run() {
- // Make sure we're running with a background priority
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
- if (Constants.DEBUG) {
- Log.d(Constants.LOG_TAG, "Flushing Disk Cache");
- }
- try {
- mDiskCache.flush();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
+ private boolean isValidOptionsForDiskCache() {
+ if (mDiskCacheEnabled) {
+ if (null == mDiskCacheLocation) {
+ Log.i(Constants.LOG_TAG,
+ "Disk Cache has been enabled, but no location given. Please call setDiskCacheLocation(...)");
+ return false;
+ }
+
+ return true;
+ }
+ return false;
+
+ }
+
+ private boolean isValidOptionsForMemoryCache() {
+ return mMemoryCacheEnabled && mMemoryCacheMaxSize > 0;
+ }
+ }
+
+ static final class DiskCacheFlushRunnable implements Runnable {
+
+ private final DiskLruCache mDiskCache;
+
+ public DiskCacheFlushRunnable(DiskLruCache cache) {
+ mDiskCache = cache;
+ }
+
+ public void run() {
+ // Make sure we're running with a background priority
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+ if (Constants.DEBUG) {
+ Log.d(Constants.LOG_TAG, "Flushing Disk Cache");
+ }
+ try {
+ mDiskCache.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
}
View
78 library/src/uk/co/senab/bitmapcache/BitmapMemoryLruCache.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2013 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,50 +12,50 @@
* 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 uk.co.senab.bitmapcache;
+import android.support.v4.util.LruCache;
+
import java.util.Map.Entry;
import java.util.Set;
-import android.support.v4.util.LruCache;
-
final class BitmapMemoryLruCache extends LruCache<String, CacheableBitmapDrawable> {
- BitmapMemoryLruCache(int maxSize) {
- super(maxSize);
- }
-
- CacheableBitmapDrawable put(CacheableBitmapDrawable value) {
- if (null != value) {
- value.setCached(true);
- return put(value.getUrl(), value);
- }
-
- return null;
- }
-
- @Override
- protected int sizeOf(String key, CacheableBitmapDrawable value) {
- return value.getMemorySize();
- }
-
- @Override
- protected void entryRemoved(boolean evicted, String key, CacheableBitmapDrawable oldValue,
- CacheableBitmapDrawable newValue) {
- // Notify the wrapper that it's no longer being cached
- oldValue.setCached(false);
- }
-
- void trimMemory() {
- final Set<Entry<String, CacheableBitmapDrawable>> values = snapshot().entrySet();
-
- for (Entry<String, CacheableBitmapDrawable> entry : values) {
- CacheableBitmapDrawable value = entry.getValue();
- if (null == value || !value.isBeingDisplayed()) {
- remove(entry.getKey());
- }
- }
- }
+ BitmapMemoryLruCache(int maxSize) {
+ super(maxSize);
+ }
+
+ CacheableBitmapDrawable put(CacheableBitmapDrawable value) {
+ if (null != value) {
+ value.setCached(true);
+ return put(value.getUrl(), value);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected int sizeOf(String key, CacheableBitmapDrawable value) {
+ return value.getMemorySize();
+ }
+
+ @Override
+ protected void entryRemoved(boolean evicted, String key, CacheableBitmapDrawable oldValue,
+ CacheableBitmapDrawable newValue) {
+ // Notify the wrapper that it's no longer being cached
+ oldValue.setCached(false);
+ }
+
+ void trimMemory() {
+ final Set<Entry<String, CacheableBitmapDrawable>> values = snapshot().entrySet();
+
+ for (Entry<String, CacheableBitmapDrawable> entry : values) {
+ CacheableBitmapDrawable value = entry.getValue();
+ if (null == value || !value.isBeingDisplayed()) {
+ remove(entry.getKey());
+ }
+ }
+ }
}
View
392 library/src/uk/co/senab/bitmapcache/CacheableBitmapDrawable.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2013 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
* 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 uk.co.senab.bitmapcache;
import android.graphics.Bitmap;
@@ -23,220 +23,212 @@
public class CacheableBitmapDrawable extends BitmapDrawable {
- static final String LOG_TAG = "CacheableBitmapDrawable";
+ static final String LOG_TAG = "CacheableBitmapDrawable";
// URL Associated with this Bitmap
- private final String mUrl;
+ private final String mUrl;
private final BitmapLruCache.RecyclePolicy mRecyclePolicy;
- // Number of Views currently displaying bitmap
- private int mDisplayingCount;
+ // Number of Views currently displaying bitmap
+ private int mDisplayingCount;
- // Has it been displayed yet
- private boolean mHasBeenDisplayed;
+ // Has it been displayed yet
+ private boolean mHasBeenDisplayed;
- // Number of caches currently referencing the wrapper
- private int mCacheCount;
+ // Number of caches currently referencing the wrapper
+ private int mCacheCount;
- // The CheckStateRunnable currently being delayed
- private Runnable mCheckStateRunnable;
+ // The CheckStateRunnable currently being delayed
+ private Runnable mCheckStateRunnable;
- // Handler which may be used later
- private static final Handler sHandler = new Handler(Looper.getMainLooper());
+ // Handler which may be used later
+ private static final Handler sHandler = new Handler(Looper.getMainLooper());
- @SuppressWarnings("deprecation")
- CacheableBitmapDrawable(String url, Bitmap bitmap, BitmapLruCache.RecyclePolicy recyclePolicy) {
- super(bitmap);
+ @SuppressWarnings("deprecation")
+ CacheableBitmapDrawable(String url, Bitmap bitmap, BitmapLruCache.RecyclePolicy recyclePolicy) {
+ super(bitmap);
- mUrl = url;
+ mUrl = url;
mRecyclePolicy = recyclePolicy;
- mDisplayingCount = 0;
- mCacheCount = 0;
- }
-
- /**
- * @return Amount of heap size currently being used by {@code Bitmap}
- */
- int getMemorySize() {
- int size = 0;
-
- final Bitmap bitmap = getBitmap();
- if (null != bitmap && !bitmap.isRecycled()) {
- size = bitmap.getRowBytes() * bitmap.getHeight();
- }
-
- return size;
- }
-
- /**
- * @return the URL associated with the BitmapDrawable
- */
- public String getUrl() {
- return mUrl;
- }
-
- /**
- * Returns true when this wrapper has a bitmap and the bitmap has not been
- * recycled.
- *
- * @return true - if the bitmap has not been recycled.
- */
- public synchronized boolean hasValidBitmap() {
- Bitmap bitmap = getBitmap();
- if (null != bitmap) {
- return !bitmap.isRecycled();
- }
- return false;
- }
-
- /**
- * @return true - if the bitmap is currently being displayed by a
- * {@link CacheableImageView}.
- */
- public synchronized boolean isBeingDisplayed() {
- return mDisplayingCount > 0;
- }
-
- /**
- * @return true - if the wrapper is currently referenced by a cache.
- */
- public synchronized boolean isReferencedByCache() {
- return mCacheCount > 0;
- }
-
- /**
- * Used to signal to the Drawable whether it is being used or not.
- *
- * @param beingUsed - true if being used, false if not.
- */
- public synchronized void setBeingUsed(boolean beingUsed) {
- if (beingUsed) {
- mDisplayingCount++;
- mHasBeenDisplayed = true;
- } else {
- mDisplayingCount--;
- }
- checkState();
- }
-
- /**
- * Used to signal to the wrapper whether it is being referenced by a cache
- * or not.
- *
- * @param added - true if the wrapper has been added to a cache, false if
- * removed.
- */
- synchronized void setCached(boolean added) {
- if (added) {
- mCacheCount++;
- } else {
- mCacheCount--;
- }
- checkState();
- }
-
- private void cancelCheckStateCallback() {
- if (null != mCheckStateRunnable) {
- if (Constants.DEBUG) {
- Log.d(LOG_TAG, "Cancelling checkState() callback for: " + mUrl);
- }
- sHandler.removeCallbacks(mCheckStateRunnable);
- mCheckStateRunnable = null;
- }
- }
-
- /**
- * Calls {@link #checkState(boolean)} with default parameter of
- * <code>false</code>.
- */
- private void checkState() {
- checkState(false);
- }
-
- /**
- * Checks whether the wrapper is currently referenced by a cache, and is
- * being displayed. If neither of those conditions are met then the bitmap
- * is ready to be recycled. Whether this happens now, or is delayed depends
- * on whether the Drawable has been displayed or not.
- * <ul>
- * <li>If it has been displayed, it is recycled straight away.</li>
- * <li>If it has not been displayed, and <code>ignoreBeenDisplayed</code> is
- * <code>false</code>, a call to <code>checkState(true)</code> is queued to
- * be called after a delay.</li>
- * <li>If it has not been displayed, and <code>ignoreBeenDisplayed</code> is
- * <code>true</code>, it is recycled straight away.</li>
- * </ul>
- *
- * @see Constants#UNUSED_DRAWABLE_RECYCLE_DELAY_MS
- *
- * @param ignoreBeenDisplayed - Whether to ignore the 'has been displayed'
- * flag when deciding whether to recycle() now.
- */
- private synchronized void checkState(final boolean ignoreBeenDisplayed) {
- if (Constants.DEBUG) {
- Log.d(LOG_TAG, String.format("checkState(). Been Displayed: %b, Displaying: %d, Caching: %d, URL: %s",
- mHasBeenDisplayed, mDisplayingCount, mCacheCount, mUrl));
- }
+ mDisplayingCount = 0;
+ mCacheCount = 0;
+ }
+
+ /**
+ * @return Amount of heap size currently being used by {@code Bitmap}
+ */
+ int getMemorySize() {
+ int size = 0;
+
+ final Bitmap bitmap = getBitmap();
+ if (null != bitmap && !bitmap.isRecycled()) {
+ size = bitmap.getRowBytes() * bitmap.getHeight();
+ }
+
+ return size;
+ }
+
+ /**
+ * @return the URL associated with the BitmapDrawable
+ */
+ public String getUrl() {
+ return mUrl;
+ }
+
+ /**
+ * Returns true when this wrapper has a bitmap and the bitmap has not been recycled.
+ *
+ * @return true - if the bitmap has not been recycled.
+ */
+ public synchronized boolean hasValidBitmap() {
+ Bitmap bitmap = getBitmap();
+ return null != bitmap && !bitmap.isRecycled();
+ }
+
+ /**
+ * @return true - if the bitmap is currently being displayed by a {@link CacheableImageView}.
+ */
+ public synchronized boolean isBeingDisplayed() {
+ return mDisplayingCount > 0;
+ }
+
+ /**
+ * @return true - if the wrapper is currently referenced by a cache.
+ */
+ public synchronized boolean isReferencedByCache() {
+ return mCacheCount > 0;
+ }
+
+ /**
+ * Used to signal to the Drawable whether it is being used or not.
+ *
+ * @param beingUsed - true if being used, false if not.
+ */
+ public synchronized void setBeingUsed(boolean beingUsed) {
+ if (beingUsed) {
+ mDisplayingCount++;
+ mHasBeenDisplayed = true;
+ } else {
+ mDisplayingCount--;
+ }
+ checkState();
+ }
+
+ /**
+ * Used to signal to the wrapper whether it is being referenced by a cache or not.
+ *
+ * @param added - true if the wrapper has been added to a cache, false if removed.
+ */
+ synchronized void setCached(boolean added) {
+ if (added) {
+ mCacheCount++;
+ } else {
+ mCacheCount--;
+ }
+ checkState();
+ }
+
+ private void cancelCheckStateCallback() {
+ if (null != mCheckStateRunnable) {
+ if (Constants.DEBUG) {
+ Log.d(LOG_TAG, "Cancelling checkState() callback for: " + mUrl);
+ }
+ sHandler.removeCallbacks(mCheckStateRunnable);
+ mCheckStateRunnable = null;
+ }
+ }
+
+ /**
+ * Calls {@link #checkState(boolean)} with default parameter of <code>false</code>.
+ */
+ private void checkState() {
+ checkState(false);
+ }
+
+ /**
+ * Checks whether the wrapper is currently referenced by a cache, and is being displayed. If
+ * neither of those conditions are met then the bitmap is ready to be recycled. Whether this
+ * happens now, or is delayed depends on whether the Drawable has been displayed or not. <ul>
+ * <li>If it has been displayed, it is recycled straight away.</li> <li>If it has not been
+ * displayed, and <code>ignoreBeenDisplayed</code> is <code>false</code>, a call to
+ * <code>checkState(true)</code> is queued to be called after a delay.</li> <li>If it has not been
+ * displayed, and <code>ignoreBeenDisplayed</code> is <code>true</code>, it is recycled straight
+ * away.</li> </ul>
+ *
+ * @param ignoreBeenDisplayed - Whether to ignore the 'has been displayed' flag when deciding
+ * whether to recycle() now.
+ * @see Constants#UNUSED_DRAWABLE_RECYCLE_DELAY_MS
+ */
+ private synchronized void checkState(final boolean ignoreBeenDisplayed) {
+ if (Constants.DEBUG) {
+ Log.d(LOG_TAG, String.format(
+ "checkState(). Been Displayed: %b, Displaying: %d, Caching: %d, URL: %s",
+ mHasBeenDisplayed, mDisplayingCount, mCacheCount, mUrl));
+ }
// If the policy doesn't let us recycle, return now
if (!mRecyclePolicy.canRecycle()) {
- return;
+ return;
}
- if (mCacheCount <= 0 && mDisplayingCount <= 0) {
- // We're not being referenced or used anywhere
-
- // Cancel the callback, if one is queued.
- cancelCheckStateCallback();
-
- if (hasValidBitmap()) {
- /**
- * If we have been displayed or we don't care whether we have
- * been or not, then recycle() now. Otherwise, we retry in 1
- * second.
- */
- if (mHasBeenDisplayed || ignoreBeenDisplayed) {
- if (Constants.DEBUG) {
- Log.d(LOG_TAG, "Recycling bitmap with url: " + mUrl);
- }
- getBitmap().recycle();
- } else {
- if (Constants.DEBUG) {
- Log.d(LOG_TAG, "Unused Bitmap which hasn't been displayed, delaying recycle(): " + mUrl);
- }
- mCheckStateRunnable = new CheckStateRunnable(this);
- sHandler.postDelayed(mCheckStateRunnable, Constants.UNUSED_DRAWABLE_RECYCLE_DELAY_MS);
- }
- }
- } else {
- // We're being referenced (by either a cache or used somewhere)
-
- /**
- * If mCheckStateRunnable isn't null, then a checkState() call has
- * been queued previously. As we're being used now, cancel the
- * callback.
- */
- cancelCheckStateCallback();
- }
- }
-
- /**
- * Runnable which run a {@link CacheableBitmapDrawable#checkState(boolean)
- * checkState(false)} call.
- *
- * @author chrisbanes
- */
- private static final class CheckStateRunnable extends WeakReferenceRunnable<CacheableBitmapDrawable> {
-
- public CheckStateRunnable(CacheableBitmapDrawable object) {
- super(object);
- }
-
- @Override
- public void run(CacheableBitmapDrawable object) {
- object.checkState(true);
- }
- }
+ if (mCacheCount <= 0 && mDisplayingCount <= 0) {
+ // We're not being referenced or used anywhere
+
+ // Cancel the callback, if one is queued.
+ cancelCheckStateCallback();
+
+ if (hasValidBitmap()) {
+ /**
+ * If we have been displayed or we don't care whether we have
+ * been or not, then recycle() now. Otherwise, we retry in 1
+ * second.
+ */
+ if (mHasBeenDisplayed || ignoreBeenDisplayed) {
+ if (Constants.DEBUG) {
+ Log.d(LOG_TAG, "Recycling bitmap with url: " + mUrl);
+ }
+ getBitmap().recycle();
+ } else {
+ if (Constants.DEBUG) {
+ Log.d(LOG_TAG,
+ "Unused Bitmap which hasn't been displayed, delaying recycle(): "
+ + mUrl);
+ }
+ mCheckStateRunnable = new CheckStateRunnable(this);
+ sHandler.postDelayed(mCheckStateRunnable,
+ Constants.UNUSED_DRAWABLE_RECYCLE_DELAY_MS);
+ }
+ }
+ } else {
+ // We're being referenced (by either a cache or used somewhere)
+
+ /**
+ * If mCheckStateRunnable isn't null, then a checkState() call has
+ * been queued previously. As we're being used now, cancel the
+ * callback.
+ */
+ cancelCheckStateCallback();
+ }
+ }
+
+ /**
+ * Runnable which run a {@link CacheableBitmapDrawable#checkState(boolean) checkState(false)}
+ * call.
+ *
+ * @author chrisbanes
+ */
+ private static final class CheckStateRunnable
+ extends WeakReferenceRunnable<CacheableBitmapDrawable> {
+
+ public CheckStateRunnable(CacheableBitmapDrawable object) {
+ super(object);
+ }
+
+ @Override
+ public void run(CacheableBitmapDrawable object) {
+ object.checkState(true);
+ }
+ }
}
View
92 library/src/uk/co/senab/bitmapcache/CacheableImageView.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2013 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
* 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 uk.co.senab.bitmapcache;
import android.content.Context;
@@ -23,59 +23,59 @@
public class CacheableImageView extends ImageView {
- private static void onDrawableSet(Drawable drawable) {
- if (drawable instanceof CacheableBitmapDrawable) {
- ((CacheableBitmapDrawable) drawable).setBeingUsed(true);
- }
- }
+ private static void onDrawableSet(Drawable drawable) {
+ if (drawable instanceof CacheableBitmapDrawable) {
+ ((CacheableBitmapDrawable) drawable).setBeingUsed(true);
+ }
+ }
- private static void onDrawableUnset(final Drawable drawable) {
- if (drawable instanceof CacheableBitmapDrawable) {
- ((CacheableBitmapDrawable) drawable).setBeingUsed(false);
- }
- }
+ private static void onDrawableUnset(final Drawable drawable) {
+ if (drawable instanceof CacheableBitmapDrawable) {
+ ((CacheableBitmapDrawable) drawable).setBeingUsed(false);
+ }
+ }
- public CacheableImageView(Context context) {
- super(context);
- }
+ public CacheableImageView(Context context) {
+ super(context);
+ }
- public CacheableImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
+ public CacheableImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
- @Override
- public void setImageDrawable(Drawable drawable) {
- final Drawable previousDrawable = getDrawable();
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ final Drawable previousDrawable = getDrawable();
- // Set new Drawable
- super.setImageDrawable(drawable);
+ // Set new Drawable
+ super.setImageDrawable(drawable);
- if (drawable != previousDrawable) {
- onDrawableSet(drawable);
- onDrawableUnset(previousDrawable);
- }
- }
+ if (drawable != previousDrawable) {
+ onDrawableSet(drawable);
+ onDrawableUnset(previousDrawable);
+ }
+ }
- @Override
- public void setImageResource(int resId) {
- final Drawable previousDrawable = getDrawable();
- super.setImageResource(resId);
- onDrawableUnset(previousDrawable);
- }
+ @Override
+ public void setImageResource(int resId) {
+ final Drawable previousDrawable = getDrawable();
+ super.setImageResource(resId);
+ onDrawableUnset(previousDrawable);
+ }
- @Override
- public void setImageURI(Uri uri) {
- final Drawable previousDrawable = getDrawable();
- super.setImageURI(uri);
- onDrawableUnset(previousDrawable);
- }
+ @Override
+ public void setImageURI(Uri uri) {
+ final Drawable previousDrawable = getDrawable();
+ super.setImageURI(uri);
+ onDrawableUnset(previousDrawable);
+ }
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
- // Will cause displayed bitmap wrapper to be 'free-able'
- setImageDrawable(null);
- }
+ // Will cause displayed bitmap wrapper to be 'free-able'
+ setImageDrawable(null);
+ }
}
View
10 library/src/uk/co/senab/bitmapcache/Constants.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2013 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,15 +12,15 @@
* 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 uk.co.senab.bitmapcache;
class Constants {
- static boolean DEBUG = false;
+ static boolean DEBUG = false;
- static String LOG_TAG = "BitmapCache";
+ static String LOG_TAG = "BitmapCache";
- static final int UNUSED_DRAWABLE_RECYCLE_DELAY_MS = 2000;
+ static final int UNUSED_DRAWABLE_RECYCLE_DELAY_MS = 2000;
}
View
57 library/src/uk/co/senab/bitmapcache/Md5.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2013 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,39 +12,40 @@
* 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 uk.co.senab.bitmapcache;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
class Md5 {
- private static final char[] DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e',
- 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
-
- public static String encode(String string) {
- try {
- MessageDigest digest = MessageDigest.getInstance("MD5");
- return bytesToHexString(digest.digest(string.getBytes()));
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- private static String bytesToHexString(byte[] bytes) {
- final char[] buf = new char[bytes.length * 2];
-
- byte b;
- int c = 0;
- for (int i = 0, z = bytes.length; i < z; i++) {
- b = bytes[i];
- buf[c++] = DIGITS[(b >> 4) & 0xf];
- buf[c++] = DIGITS[b & 0xf];
- }
-
- return new String(buf);
- }
+ private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
+ 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
+ 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
+
+ public static String encode(String string) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("MD5");
+ return bytesToHexString(digest.digest(string.getBytes()));
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private static String bytesToHexString(byte[] bytes) {
+ final char[] buf = new char[bytes.length * 2];
+
+ byte b;
+ int c = 0;
+ for (int i = 0, z = bytes.length; i < z; i++) {
+ b = bytes[i];
+ buf[c++] = DIGITS[(b >> 4) & 0xf];
+ buf[c++] = DIGITS[b & 0xf];
+ }
+
+ return new String(buf);
+ }
}
View
66 library/src/uk/co/senab/bitmapcache/Util.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2012 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,51 +12,45 @@
* 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 uk.co.senab.bitmapcache;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-
class Util {
- static long copy(File in, OutputStream out) throws IOException {
- return copy(new FileInputStream(in), out);
- }
-
- static long copy(InputStream in, File out) throws IOException {
- return copy(in, new FileOutputStream(out));
- }
-
- static void saveBitmap(Bitmap bitmap, OutputStream out) {
- bitmap.compress(CompressFormat.PNG, 100, out);
- }
-
- /**
- * Pipe an InputStream to the given OutputStream
- * <p />
- * Taken from Apache Commons IOUtils.
- *
- * @param in
- * @param out
- * @throws IOException
- */
- private static long copy(InputStream input, OutputStream output) throws IOException {
- byte[] buffer = new byte[1024 * 4];
- long count = 0;
- int n = 0;
- while (-1 != (n = input.read(buffer))) {
- output.write(buffer, 0, n);
- count += n;
- }
- return count;
- }
+ static long copy(File in, OutputStream out) throws IOException {
+ return copy(new FileInputStream(in), out);
+ }
+
+ static long copy(InputStream in, File out) throws IOException {
+ return copy(in, new FileOutputStream(out));
+ }
+
+ static void saveBitmap(Bitmap bitmap, OutputStream out) {
+ bitmap.compress(CompressFormat.PNG, 100, out);
+ }
+
+ /**
+ * Pipe an InputStream to the given OutputStream <p /> Taken from Apache Commons IOUtils.
+ */
+ private static long copy(InputStream input, OutputStream output) throws IOException {
+ byte[] buffer = new byte[1024 * 4];
+ long count = 0;
+ int n;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ count += n;
+ }
+ return count;
+ }
}
View
28 library/src/uk/co/senab/bitmapcache/WeakReferenceRunnable.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2013 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,28 +12,28 @@
* 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 uk.co.senab.bitmapcache;
import java.lang.ref.WeakReference;
abstract class WeakReferenceRunnable<T> implements Runnable {
- private final WeakReference<T> mObjectRef;
+ private final WeakReference<T> mObjectRef;
- public WeakReferenceRunnable(T object) {
- mObjectRef = new WeakReference<T>(object);
- }
+ public WeakReferenceRunnable(T object) {
+ mObjectRef = new WeakReference<T>(object);
+ }
- @Override
- public final void run() {
- T object = mObjectRef.get();
+ @Override
+ public final void run() {
+ T object = mObjectRef.get();
- if (null != object) {
- run(object);
- }
- }
+ if (null != object) {
+ run(object);
+ }
+ }
- public abstract void run(T object);
+ public abstract void run(T object);
}
View
130 sample/src/uk/co/senab/bitmapcache/samples/GridViewActivity.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright 2011, 2013 Chris Banes.
+ * Copyright (c) 2013 Chris Banes.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -12,9 +12,18 @@
* 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 uk.co.senab.bitmapcache.samples;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.widget.GridView;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -25,88 +34,79 @@
import java.util.ArrayList;
import java.util.HashSet;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.app.Activity;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.widget.GridView;
-
public class GridViewActivity extends Activity {
- static final int PUG_COUNT = 60;
+ static final int PUG_COUNT = 60;
- /**
- * This task simply gets a list of URLs of Photos from PugMe
- */
- private class PugListAsyncTask extends AsyncTask<Void, Void, ArrayList<String>> {
+ /**
+ * This task simply gets a list of URLs of Photos from PugMe
+ */
+ private class PugListAsyncTask extends AsyncTask<Void, Void, ArrayList<String>> {
- static final String PUG_ME_URL = "http://pugme.herokuapp.com/bomb?count=" + PUG_COUNT;
+ static final String PUG_ME_URL = "http://pugme.herokuapp.com/bomb?count=" + PUG_COUNT;
- @Override
- protected ArrayList<String> doInBackground(Void... params) {
- try {
- HttpURLConnection conn = (HttpURLConnection) new URL(PUG_ME_URL).openConnection();
- conn.setRequestProperty("Accept", "application/json");
- InputStream is = conn.getInputStream();
+ @Override
+ protected ArrayList<String> doInBackground(Void... params) {
+ try {
+ HttpURLConnection conn = (HttpURLConnection) new URL(PUG_ME_URL).openConnection();
+ conn.setRequestProperty("Accept", "application/json");
+ InputStream is = conn.getInputStream();
- StringBuilder sb = new StringBuilder();
- BufferedReader r = new BufferedReader(new InputStreamReader(is), 1024);
- for (String line = r.readLine(); line != null; line = r.readLine()) {
- sb.append(line);
- }
- try {
- is.close();
- } catch (IOException e) {
- }
+ StringBuilder sb = new StringBuilder();
+ BufferedReader r = new BufferedReader(new InputStreamReader(is), 1024);
+ for (String line = r.readLine(); line != null; line = r.readLine()) {
+ sb.append(line);
+ }
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
- String response = sb.toString();
- JSONObject document = new JSONObject(response);
+ String response = sb.toString();
+ JSONObject document = new JSONObject(response);
- JSONArray pugsJsonArray = document.getJSONArray("pugs");
- HashSet<String> pugUrls = new HashSet<String>(pugsJsonArray.length());
+ JSONArray pugsJsonArray = document.getJSONArray("pugs");
+ HashSet<String> pugUrls = new HashSet<String>(pugsJsonArray.length());
- for (int i = 0, z = pugsJsonArray.length(); i < z; i++) {
- pugUrls.add(pugsJsonArray.getString(i));
- }
+ for (int i = 0, z = pugsJsonArray.length(); i < z; i++) {
+ pugUrls.add(pugsJsonArray.getString(i));
+ }
- return new ArrayList<String>(pugUrls);
+ return new ArrayList<String>(pugUrls);
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (JSONException e) {
- e.printStackTrace();
- }
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();