Permalink
Browse files

Merge tag 'v2.3'

[maven-release-plugin]  copy for tag v2.3
  • Loading branch information...
2 parents e38f273 + 42027f9 commit 147e5c078324c8f61765750da756c4a7d1f2aae3 @chrisbanes committed Aug 3, 2013
View
@@ -42,7 +42,7 @@ it distributed to the central repositories. Simply add the following to your
<dependency>
<groupId>com.github.chrisbanes.bitmapcache</groupId>
<artifactId>library</artifactId>
- <version>2.2.2</version>
+ <version>(check pom.xml for latest version)</version>
</dependency>
```
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uk.co.senab.bitmapcache"
- android:versionCode="2220"
- android:versionName="2.2.2" >
+ android:versionCode="2300"
+ android:versionName="2.3" >
<uses-sdk android:minSdkVersion="4" />
View
@@ -10,7 +10,7 @@
<parent>
<groupId>com.github.chrisbanes.bitmapcache</groupId>
<artifactId>parent</artifactId>
- <version>2.2.3-SNAPSHOT</version>
+ <version>2.3</version>
</parent>
<dependencies>
@@ -29,6 +29,8 @@
import android.util.Log;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -45,7 +47,7 @@
*
* <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.
+ * CacheableImageView} wherever possible, as the memory cache has a closeStream relationship with it.
* </p>
*
* <p> Clients can call {@link #get(String)} to retrieve a cached value from the given Url. This
@@ -84,6 +86,15 @@
*/
ALWAYS;
+ boolean canInBitmap() {
+ switch (this) {
+ case PRE_HONEYCOMB_ONLY:
+ case DISABLED:
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
+ }
+ return false;
+ }
+
boolean canRecycle() {
switch (this) {
case DISABLED:
@@ -257,24 +268,18 @@ public CacheableBitmapDrawable getFromDiskCache(final String url,
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, mResources, 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();
+ // Try and decode bitmap
+ result = decodeBitmap(new SnapshotInputStreamProvider(key), url, decodeOpts);
+
+ if (null != result) {
+ 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();
@@ -301,7 +306,7 @@ public CacheableBitmapDrawable getFromMemoryCache(final String url) {
result = mMemoryCache.get(url);
// If we get a value, but it has a invalid bitmap, remove it
- if (null != result && !result.hasValidBitmap()) {
+ if (null != result && !result.isBitmapValid()) {
mMemoryCache.remove(url);
result = null;
}
@@ -355,7 +360,7 @@ public CacheableBitmapDrawable put(final String url, final Bitmap bitmap,
Bitmap.CompressFormat compressFormat, int compressQuality) {
CacheableBitmapDrawable d = new CacheableBitmapDrawable(url, mResources, bitmap,
- mRecyclePolicy);
+ mRecyclePolicy, CacheableBitmapDrawable.SOURCE_UNKNOWN);
if (null != mMemoryCache) {
mMemoryCache.put(d);
@@ -379,14 +384,7 @@ public CacheableBitmapDrawable put(final String url, final Bitmap bitmap,
} catch (IOException e) {
Log.e(Constants.LOG_TAG, "Error while writing to disk cache", e);
} finally {
- if (null != os) {
- try {
- os.close();
- } catch (IOException e) {
- Log.e(Constants.LOG_TAG, "Failed to close output stream", e);
- }
- }
-
+ IoUtils.closeStream(os);
lock.unlock();
scheduleDiskCacheFlush();
}
@@ -448,11 +446,9 @@ public CacheableBitmapDrawable put(final String url, final InputStream inputStre
if (null != tmpFile) {
// Try and decode File
- Bitmap bitmap = BitmapFactory.decodeFile(tmpFile.getAbsolutePath(), decodeOpts);
-
- if (null != bitmap) {
- d = new CacheableBitmapDrawable(url, mResources, bitmap, mRecyclePolicy);
+ d = decodeBitmap(new FileInputStreamProvider(tmpFile), url, decodeOpts);
+ if (d != null) {
if (null != mMemoryCache) {
d.setCached(true);
mMemoryCache.put(d.getUrl(), d);
@@ -525,9 +521,9 @@ synchronized void setDiskCache(DiskLruCache diskCache) {
}
}
- void setMemoryCache(BitmapMemoryLruCache memoryCache, RecyclePolicy recyclePolicy) {
+ void setMemoryCache(BitmapMemoryLruCache memoryCache) {
mMemoryCache = memoryCache;
- mRecyclePolicy = recyclePolicy;
+ mRecyclePolicy = memoryCache.getRecyclePolicy();
}
private ReentrantLock getLockForDiskCacheEdit(String url) {
@@ -553,6 +549,71 @@ private void scheduleDiskCacheFlush() {
TimeUnit.SECONDS);
}
+ private CacheableBitmapDrawable decodeBitmap(InputStreamProvider ip, String url,
+ BitmapFactory.Options opts) {
+
+ Bitmap bm = null;
+ InputStream is = null;
+ int source = CacheableBitmapDrawable.SOURCE_NEW;
+
+ try {
+ if (mRecyclePolicy.canInBitmap()) {
+ // Create an options instance if we haven't been provided with one
+ if (opts == null) {
+ opts = new BitmapFactory.Options();
+ }
+
+ if (opts.inSampleSize <= 1) {
+ opts.inSampleSize = 1;
+
+ if (addInBitmapOptions(ip, opts)) {
+ source = CacheableBitmapDrawable.SOURCE_INBITMAP;
+ }
+ }
+ }
+
+ // Get InputStream for actual decode
+ is = ip.getInputStream();
+ // Decode stream
+ bm = BitmapFactory.decodeStream(is, null, opts);
+ } catch (Exception e) {
+ Log.e(Constants.LOG_TAG, "Unable to decode stream", e);
+ } finally {
+ IoUtils.closeStream(is);
+ }
+
+ if (bm != null) {
+ return new CacheableBitmapDrawable(url, mResources, bm, mRecyclePolicy, source);
+ }
+ return null;
+ }
+
+ private boolean addInBitmapOptions(InputStreamProvider ip, BitmapFactory.Options opts) {
+ // Create InputStream for decoding the bounds
+ final InputStream is = ip.getInputStream();
+ // Decode the bounds so we know what size Bitmap to look for
+ opts.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(is, null, opts);
+ IoUtils.closeStream(is);
+
+ // Turn off just decoding bounds
+ opts.inJustDecodeBounds = false;
+ // Make sure the decoded file is mutable
+ opts.inMutable = true;
+
+ // Try and find Bitmap to use for inBitmap
+ Bitmap reusableBm = mMemoryCache.getBitmapFromRemoved(opts.outWidth, opts.outHeight);
+ if (reusableBm != null) {
+ if (Constants.DEBUG) {
+ Log.i(Constants.LOG_TAG, "Using inBitmap");
+ }
+ SDK11.addInBitmapOption(opts, reusableBm);
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Builder class for {link {@link BitmapLruCache}. An example call:
*
@@ -578,7 +639,7 @@ private void scheduleDiskCacheFlush() {
static final int DEFAULT_MEM_CACHE_MAX_SIZE_MB = 3;
- static final RecyclePolicy DEFAULT_RECYCLE_POLICY = RecyclePolicy.ALWAYS;
+ static final RecyclePolicy DEFAULT_RECYCLE_POLICY = RecyclePolicy.PRE_HONEYCOMB_ONLY;
// Only used for Javadoc
static final float DEFAULT_MEMORY_CACHE_HEAP_PERCENTAGE = DEFAULT_MEMORY_CACHE_HEAP_RATIO
@@ -635,7 +696,7 @@ public BitmapLruCache build() {
if (Constants.DEBUG) {
Log.d("BitmapLruCache.Builder", "Creating Memory Cache");
}
- cache.setMemoryCache(new BitmapMemoryLruCache(mMemoryCacheMaxSize), mRecyclePolicy);
+ cache.setMemoryCache(new BitmapMemoryLruCache(mMemoryCacheMaxSize, mRecyclePolicy));
}
if (isValidOptionsForDiskCache()) {
@@ -799,4 +860,47 @@ public void run() {
}
}
}
+
+ interface InputStreamProvider {
+ InputStream getInputStream();
+ }
+
+ static class FileInputStreamProvider implements InputStreamProvider {
+ final File mFile;
+
+ FileInputStreamProvider(File file) {
+ mFile = file;
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ try {
+ return new FileInputStream(mFile);
+ } catch (FileNotFoundException e) {
+ Log.e(Constants.LOG_TAG, "Could not decode file: " + mFile.getAbsolutePath(), e);
+ }
+ return null;
+ }
+ }
+
+ final class SnapshotInputStreamProvider implements InputStreamProvider {
+ final String mKey;
+
+ SnapshotInputStreamProvider(String key) {
+ mKey = key;
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ try {
+ DiskLruCache.Snapshot snapshot = mDiskCache.get(mKey);
+ if (snapshot != null) {
+ return snapshot.getInputStream(0);
+ }
+ } catch (IOException e) {
+ Log.e(Constants.LOG_TAG, "Could open disk cache for url: " + mKey, e);
+ }
+ return null;
+ }
+ }
}
@@ -15,15 +15,28 @@
******************************************************************************/
package uk.co.senab.bitmapcache;
+import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
+import java.lang.ref.SoftReference;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
final class BitmapMemoryLruCache extends LruCache<String, CacheableBitmapDrawable> {
- BitmapMemoryLruCache(int maxSize) {
+ private final Set<SoftReference<CacheableBitmapDrawable>> mRemovedEntries;
+ private final BitmapLruCache.RecyclePolicy mRecyclePolicy;
+
+ BitmapMemoryLruCache(int maxSize, BitmapLruCache.RecyclePolicy policy) {
super(maxSize);
+
+ mRecyclePolicy = policy;
+ mRemovedEntries = policy.canInBitmap()
+ ? Collections.synchronizedSet(new HashSet<SoftReference<CacheableBitmapDrawable>>())
+ : null;
}
CacheableBitmapDrawable put(CacheableBitmapDrawable value) {
@@ -35,6 +48,10 @@ CacheableBitmapDrawable put(CacheableBitmapDrawable value) {
return null;
}
+ BitmapLruCache.RecyclePolicy getRecyclePolicy() {
+ return mRecyclePolicy;
+ }
+
@Override
protected int sizeOf(String key, CacheableBitmapDrawable value) {
return value.getMemorySize();
@@ -45,6 +62,41 @@ protected void entryRemoved(boolean evicted, String key, CacheableBitmapDrawable
CacheableBitmapDrawable newValue) {
// Notify the wrapper that it's no longer being cached
oldValue.setCached(false);
+
+ if (mRemovedEntries != null && oldValue.isBitmapValid() && oldValue.isBitmapMutable()) {
+ synchronized (mRemovedEntries) {
+ mRemovedEntries.add(new SoftReference<CacheableBitmapDrawable>(oldValue));
+ }
+ }
+ }
+
+ Bitmap getBitmapFromRemoved(final int width, final int height) {
+ if (mRemovedEntries == null) {
+ return null;
+ }
+
+ Bitmap result = null;
+
+ synchronized (mRemovedEntries) {
+ final Iterator<SoftReference<CacheableBitmapDrawable>> it = mRemovedEntries.iterator();
+
+ while (it.hasNext()) {
+ CacheableBitmapDrawable value = it.next().get();
+
+ if (value != null && value.isBitmapValid() && value.isBitmapMutable()) {
+ if (value.getIntrinsicWidth() == width
+ && value.getIntrinsicHeight() == height) {
+ it.remove();
+ result = value.getBitmap();
+ break;
+ }
+ } else {
+ it.remove();
+ }
+ }
+ }
+
+ return result;
}
void trimMemory() {
Oops, something went wrong.

0 comments on commit 147e5c0

Please sign in to comment.