Skip to content
This repository has been archived by the owner on Oct 15, 2018. It is now read-only.

Commit

Permalink
More implementation of using Disk Cache. It's getting there.
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Banes committed Dec 16, 2012
1 parent 683b32d commit 0cd0342
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 91 deletions.
219 changes: 150 additions & 69 deletions library/src/uk/co/senab/bitmapcache/BitmapLruCache.java
Expand Up @@ -9,32 +9,69 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;

import com.jakewharton.DiskLruCache;
import com.jakewharton.DiskLruCache.Editor;

public class BitmapLruCache {

private DiskLruCache mDiskCache;
private BitmapMemoryLruCache mMemoryCache;

void setDiskLruCache(DiskLruCache cache) {
mDiskCache = cache;
private static String transformUrlForDiskCacheKey(String url) {
return Util.md5(url);
}

void setMemoryLruCache(BitmapMemoryLruCache cache) {
mMemoryCache = cache;
}
private DiskLruCache mDiskCache;
private BitmapMemoryLruCache mMemoryCache;

public CacheableBitmapWrapper get(String url) {
if (null != mMemoryCache) {
return mMemoryCache.get(url);
}

if (null != mDiskCache) {
try {
DiskLruCache.Snapshot snapshot = mDiskCache.get(transformUrlForDiskCacheKey(url));
if (null != snapshot) {
// Try and decode bitmap
Bitmap bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));
if (null != bitmap) {
CacheableBitmapWrapper wrapper = new CacheableBitmapWrapper(url, bitmap);
putIntoMemoryCache(wrapper);
return wrapper;
} else {
// TODO Remove from Disk Cache?
}
}
} catch (IOException e) {
e.printStackTrace();
}
}

return null;
}

public void put(String url, InputStream inputStream) {
public CacheableBitmapWrapper put(String url, Bitmap bitmap) {
CacheableBitmapWrapper wrapper = new CacheableBitmapWrapper(url, bitmap);

if (null != mDiskCache) {
try {
DiskLruCache.Editor editor = mDiskCache.edit(transformUrlForDiskCacheKey(url));
if (null != editor) {
Util.saveBitmap(bitmap, editor.newOutputStream(0));
editor.commit();
}
} catch (IOException e) {
e.printStackTrace();
}
}

if (null != mMemoryCache) {
putIntoMemoryCache(wrapper);
}

return wrapper;
}

public CacheableBitmapWrapper put(String url, InputStream inputStream) {
// First we need to save the stream contents to a temporary file, so it
// can be read multiple times
File tmpFile = null;
Expand All @@ -54,10 +91,14 @@ public void put(String url, InputStream inputStream) {
e.printStackTrace();
}

CacheableBitmapWrapper wrapper = null;

if (null != tmpFile) {
wrapper = new CacheableBitmapWrapper(url, BitmapFactory.decodeFile(tmpFile.getAbsolutePath()));

if (null != mDiskCache) {
try {
Editor editor = mDiskCache.edit(url);
DiskLruCache.Editor editor = mDiskCache.edit(transformUrlForDiskCacheKey(url));
if (null != editor) {
Util.pipe(inputStream, editor.newOutputStream(0));
editor.commit();
Expand All @@ -68,30 +109,15 @@ public void put(String url, InputStream inputStream) {
}

if (null != mMemoryCache) {
putIntoMemoryCache(url, BitmapFactory.decodeFile(tmpFile.getAbsolutePath()));
wrapper.setCached(true);
mMemoryCache.put(wrapper.getUrl(), wrapper);
}

// Finally, delete the temporary file
tmpFile.delete();
}
}

public void put(String url, Bitmap bitmap) {
if (null != mDiskCache) {
try {
Editor editor = mDiskCache.edit(url);
if (null != editor) {
Util.saveBitmap(bitmap, editor.newOutputStream(0));
editor.commit();
}
} catch (IOException e) {
e.printStackTrace();
}
}

if (null != mMemoryCache) {
putIntoMemoryCache(url, bitmap);
}
return wrapper;
}

/**
Expand All @@ -106,70 +132,125 @@ public void trimMemory() {
}
}

private void putIntoMemoryCache(String url, Bitmap bitmap) {
if (null != bitmap) {
CacheableBitmapWrapper wrapper = new CacheableBitmapWrapper(url, bitmap);
wrapper.setCached(true);
mMemoryCache.put(url, wrapper);
} else {
// TODO Add log here
}
void setDiskLruCache(DiskLruCache cache) {
mDiskCache = cache;
}

void setMemoryLruCache(BitmapMemoryLruCache cache) {
mMemoryCache = cache;
}

private void putIntoMemoryCache(CacheableBitmapWrapper wrapper) {
wrapper.setCached(true);
mMemoryCache.put(wrapper.getUrl(), wrapper);
}

public static class Builder {

static final float DEFAULT_CACHE_SIZE = 1f / 8f;
static final float MAX_CACHE_SIZE = 0.75f;
static final int MEGABYTE = 1024 * 1024;

private boolean mValidMemoryCache;
private boolean mValidDiskCache;
static final float DEFAULT_MEMORY_CACHE_HEAP_RATIO = 1f / 8f;
static final int DEFAULT_DISK_CACHE_MAX_SIZE = MEGABYTE * 10;

static final float MAX_MEMORY_CACHE_HEAP_RATIO = 0.75f;

private static int getHeapSize(Context context) {
return ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
}

private boolean mDiskCacheEnabled;
private File mDiskCacheLocation;
private long mDiskCacheMaxSize;

private boolean mMemoryCacheEnabled;
private int mMemoryCacheMaxSize;

public Builder() {
mDiskCacheMaxSize = DEFAULT_DISK_CACHE_MAX_SIZE;

// Memory Cache is enabled by default
mMemoryCacheEnabled = true;
}

public BitmapLruCache build() {
BitmapLruCache cache = new BitmapLruCache();

if (isValidOptionsForMemoryCache()) {
Log.d("BitmapLruCache.Builder", "Creating Memory Cache");
BitmapMemoryLruCache memoryCache = new BitmapMemoryLruCache(mMemoryCacheMaxSize);
cache.setMemoryLruCache(memoryCache);
}

if (isValidOptionsForDiskCache()) {
try {
DiskLruCache diskCache = DiskLruCache.open(mDiskCacheLocation, 0, 1, mDiskCacheMaxSize);
cache.setDiskLruCache(diskCache);
Log.d("BitmapLruCache.Builder", "Created Memory Cache");
} catch (IOException e) {
e.printStackTrace();
}
}

return cache;
}

public Builder setDiskCacheEnabled(boolean enabled) {
mDiskCacheEnabled = enabled;
return this;
}

public Builder setDiskCacheLocation(File location) {
mDiskCacheLocation = location;
return this;
}

public Builder setDiskCacheMaxSize(long maxSize) {
mDiskCacheMaxSize = maxSize;
return this;
}

private int mMemoryCacheSize;
public Builder setMemoryCacheEnabled(boolean enabled) {
mMemoryCacheEnabled = enabled;
return this;
}

public Builder setMemoryCacheSize(int size) {
mMemoryCacheSize = size;
mValidMemoryCache = true;
public Builder setMemoryCacheMaxSize(int size) {
mMemoryCacheMaxSize = size;
return this;
}

/**
* Sets the Memory Cache size to be the given percentage of heap size.
* This is capped at {@value #MAX_CACHE_SIZE} denoting 75% of the app
* heap size.
* Initialise LruCache with default size of
* {@value #DEFAULT_MEMORY_CACHE_HEAP_RATIO} of heap size.
*
* @param context - Context
* @param percentageOfHeap - percentage of heap size. Valid values are
* 0.0 <= x <= {@value #MAX_CACHE_SIZE}.
* @param context - context
*/
public Builder setMemoryCacheSize(Context context, float percentageOfHeap) {
int size = Math.round(MEGABYTE * getHeapSize(context) * Math.min(percentageOfHeap, MAX_CACHE_SIZE));
return setMemoryCacheSize(size);
public Builder setMemoryCacheMaxSizeUsingHeapSize(Context context) {
return setMemoryCacheMaxSizeUsingHeapSize(context, DEFAULT_MEMORY_CACHE_HEAP_RATIO);
}

/**
* Initialise LruCache with default size of {@value #DEFAULT_CACHE_SIZE}
* of heap size.
* Sets the Memory Cache size to be the given percentage of heap size.
* This is capped at {@value #MAX_MEMORY_CACHE_HEAP_RATIO} denoting 75%
* of the app heap size.
*
* @param context - context
* @param context - Context
* @param percentageOfHeap - percentage of heap size. Valid values are
* 0.0 <= x <= {@value #MAX_MEMORY_CACHE_HEAP_RATIO}.
*/
public Builder setMemoryCacheSize(Context context) {
return setMemoryCacheSize(context, DEFAULT_CACHE_SIZE);
public Builder setMemoryCacheMaxSizeUsingHeapSize(Context context, float percentageOfHeap) {
int size = Math.round(MEGABYTE * getHeapSize(context)
* Math.min(percentageOfHeap, MAX_MEMORY_CACHE_HEAP_RATIO));
return setMemoryCacheMaxSize(size);
}

public BitmapLruCache build() {
BitmapLruCache cache = new BitmapLruCache();

if (mValidMemoryCache) {
BitmapMemoryLruCache memoryCache = new BitmapMemoryLruCache(mMemoryCacheSize);
cache.setMemoryLruCache(memoryCache);
}
private boolean isValidOptionsForDiskCache() {
return mDiskCacheEnabled && null != mDiskCacheLocation;

return cache;
}

private static int getHeapSize(Context context) {
return ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
private boolean isValidOptionsForMemoryCache() {
return mMemoryCacheEnabled && mMemoryCacheMaxSize > 0;
}
}
}
Expand Up @@ -31,11 +31,11 @@ public class CacheableBitmapWrapper {
// Number of caches currently referencing the wrapper
private int mCacheCount;

public CacheableBitmapWrapper(Bitmap bitmap) {
CacheableBitmapWrapper(Bitmap bitmap) {
this(null, bitmap);
}

public CacheableBitmapWrapper(String url, Bitmap bitmap) {
CacheableBitmapWrapper(String url, Bitmap bitmap) {
if (null == bitmap) {
throw new IllegalArgumentException("Bitmap can not be null");
}
Expand Down
23 changes: 23 additions & 0 deletions library/src/uk/co/senab/bitmapcache/Util.java
Expand Up @@ -3,6 +3,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
Expand Down Expand Up @@ -30,4 +32,25 @@ public static void saveBitmap(Bitmap bitmap, OutputStream out) {
bitmap.compress(CompressFormat.PNG, 100, out);
}

public static String md5(String string) {
try {
// Create MD5 Hash
MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
digest.update(string.getBytes());

byte messageDigest[] = digest.digest();

// Create Hex String
StringBuffer hexString = new StringBuffer();
for (int i = 0, z = messageDigest.length; i < z; i++) {
hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}
return hexString.toString();

} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}

}
1 change: 1 addition & 0 deletions sample/AndroidManifest.xml
Expand Up @@ -8,6 +8,7 @@
android:targetSdkVersion="14" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<application
android:name=".SampleApplication"
Expand Down
Expand Up @@ -26,8 +26,6 @@
import uk.co.senab.bitmapcache.CacheableBitmapWrapper;
import uk.co.senab.bitmapcache.CacheableImageView;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.AttributeSet;

Expand Down Expand Up @@ -63,16 +61,8 @@ protected CacheableBitmapWrapper doInBackground(String... params) {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
InputStream is = new BufferedInputStream(conn.getInputStream());

BitmapFactory.Options opts = new BitmapFactory.Options();
if (!mFullSize) {
opts.inSampleSize = 2;
}

Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);

if (null != bitmap) {
return new CacheableBitmapWrapper(url, bitmap);
}
// Add to cache
return mCache.put(url, is);

} catch (MalformedURLException e) {
e.printStackTrace();
Expand All @@ -87,13 +77,8 @@ protected CacheableBitmapWrapper doInBackground(String... params) {
protected void onPostExecute(CacheableBitmapWrapper result) {
super.onPostExecute(result);

if (null != result) {
// Display the image
setImageCachedBitmap(result);

// Add to cache
mCache.put(result);
}
// Display the image
setImageCachedBitmap(result);
}
}

Expand Down

0 comments on commit 0cd0342

Please sign in to comment.