From 1fa2827744ee8ffdd69384cd81bd06b82b0f204a Mon Sep 17 00:00:00 2001 From: Adrian Ulrich Date: Wed, 23 Dec 2015 16:03:40 +0100 Subject: [PATCH] Move in-memory LRU cache from CoverCache into LazyCoverView --- .../android/vanilla/CoverCache.java | 104 +++--------------- .../android/vanilla/LazyCoverView.java | 65 +++++++---- .../blinkenlights/android/vanilla/Song.java | 3 +- 3 files changed, 59 insertions(+), 113 deletions(-) diff --git a/src/ch/blinkenlights/android/vanilla/CoverCache.java b/src/ch/blinkenlights/android/vanilla/CoverCache.java index ec1e6565..6c395ba7 100644 --- a/src/ch/blinkenlights/android/vanilla/CoverCache.java +++ b/src/ch/blinkenlights/android/vanilla/CoverCache.java @@ -27,7 +27,6 @@ import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.net.Uri; -import android.util.LruCache; import android.util.Log; import java.io.ByteArrayOutputStream; @@ -41,8 +40,9 @@ import java.util.Random; public class CoverCache { /** * Returned size of small album covers + * This is 44sp * 2 = xhdpi resolution. We should probably calculate this dynamically */ - public final static int SIZE_SMALL = 96; + public final static int SIZE_SMALL = 88; /** * Returned size of large (cover view) album covers */ @@ -63,10 +63,6 @@ public class CoverCache { * Use vanilla musics SHADOW cover load mechanism */ public static final int COVER_MODE_SHADOW = 0x4; - /** - * Shared LRU cache class - */ - private static BitmapLruCache sBitmapLruCache; /** * Shared on-disk cache class */ @@ -83,9 +79,6 @@ public class CoverCache { * @param context A context to use */ public CoverCache(Context context) { - if (sBitmapLruCache == null) { - sBitmapLruCache = new BitmapLruCache(context, 6*1024*1024); - } if (sBitmapDiskCache == null) { sBitmapDiskCache = new BitmapDiskCache(context, 25*1024*1024); } @@ -98,48 +91,17 @@ public class CoverCache { * @param song The song used to identify the artwork to load * @return a bitmap or null if no artwork was found */ - public Bitmap getCoverFromSong(CoverKey key, Song song) { - Bitmap cover = getCachedCover(key); - + public Bitmap getCoverFromSong(Song song, int size) { + CoverKey key = new CoverCache.CoverKey(MediaUtils.TYPE_ALBUM, song.albumId, size); + Bitmap cover = getStoredCover(key); if (cover == null) { - // memory miss: check disk - cover = getStoredCover(key); - if (cover == null) { - // disk miss: create - cover = sBitmapLruCache.createBitmap(song, key.coverSize*key.coverSize); - if (cover != null) { - storeCover(key, cover); - } - } - // store in memory if cover was re-created - if (cover != null) { - cacheCover(key, cover); - } + cover = sBitmapDiskCache.createBitmap(song, size*size); + if (cover != null) + storeCover(key, cover); } return cover; } - /** - * Returns a cached version of the cover. - * - * @param key The cache key to use - * @param bitmap or null on cache miss - */ - public Bitmap getCachedCover(CoverKey key) { - return sBitmapLruCache.get(key); - } - - /** - * Stores a new entry in the in-memory cache - * Use getCachedCover to read the cached contents back - * - * @param key The cache key to use - * @param cover The bitmap to store - */ - public void cacheCover(CoverKey key, Bitmap cover) { - sBitmapLruCache.put(key, cover); - } - /** * Returns the on-disk cached version of the cover. * Should only be used on a background thread @@ -147,7 +109,7 @@ public class CoverCache { * @param key The cache key to use * @return bitmap or null on cache miss */ - public Bitmap getStoredCover(CoverKey key) { + private Bitmap getStoredCover(CoverKey key) { return sBitmapDiskCache.get(key); } @@ -166,9 +128,6 @@ public class CoverCache { * Deletes all items hold in the cover caches */ public static void evictAll() { - if (sBitmapLruCache != null) { - sBitmapLruCache.evictAll(); - } if (sBitmapDiskCache != null) { sBitmapDiskCache.evictAll(); } @@ -215,6 +174,10 @@ public class CoverCache { private static class BitmapDiskCache extends SQLiteOpenHelper { + /** + * The Context to use + */ + private final Context mContext; /** * Maximal cache size to use in bytes */ @@ -245,6 +208,7 @@ public class CoverCache { public BitmapDiskCache(Context context, long cacheSize) { super(context, "covercache.db", null, 1 /* version */); mCacheSize = cacheSize; + mContext = context; } /** @@ -404,44 +368,6 @@ public class CoverCache { return cover; } - } - - /** - * The LRU cache implementation, using the CoverKey as key to store Bitmap objects - * - * Note that the implementation does not override create() in order to enable - * the use of fetch-if-cached functions: createBitmap() is therefore called - * by CoverCache itself. - */ - private static class BitmapLruCache extends LruCache { - /** - * Context required for content resolver - */ - private final Context mContext; - /** - * Filenames we consider to be cover art - */ - private final static String[] coverNames = { "cover.jpg", "cover.png", "album.jpg", "album.png", "artwork.jpg", "artwork.png", "art.jpg", "art.png" }; - - /** - * Creates a new in-memory LRU cache - * - * @param context the application context - * @param size the lru cache size in bytes - */ - public BitmapLruCache(Context context, int size) { - super(size); - mContext = context; - } - - /** - * Returns the cache size in bytes, not objects - */ - @Override - protected int sizeOf(CoverKey key, Bitmap value) { - return value.getByteCount(); - } - /** * Attempts to create a new bitmap object for given song. * Returns null if no cover art was found @@ -450,7 +376,7 @@ public class CoverCache { * @param maxPxCount the maximum amount of pixels to return (30*30 = 900) */ public Bitmap createBitmap(Song song, long maxPxCount) { - + final String[] coverNames = { "cover.jpg", "cover.png", "album.jpg", "album.png", "artwork.jpg", "artwork.png", "art.jpg", "art.png" }; if (song.id < 0) { // Unindexed song: return early return null; diff --git a/src/ch/blinkenlights/android/vanilla/LazyCoverView.java b/src/ch/blinkenlights/android/vanilla/LazyCoverView.java index f4ca3ea0..b7c12c86 100644 --- a/src/ch/blinkenlights/android/vanilla/LazyCoverView.java +++ b/src/ch/blinkenlights/android/vanilla/LazyCoverView.java @@ -28,6 +28,7 @@ import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.util.AttributeSet; +import android.util.LruCache; import android.widget.ImageView; /** @@ -62,6 +63,10 @@ public class LazyCoverView extends ImageView * Cover LRU cache LRU cache */ private static CoverCache sCoverCache; + /** + * Our private LRU cache + */ + private static BitmapLruCache sBitmapLruCache; /** * The cover key we are expected to draw */ @@ -97,6 +102,9 @@ public class LazyCoverView extends ImageView if (sCoverCache == null) { sCoverCache = new CoverCache(mContext); } + if (sBitmapLruCache == null) { + sBitmapLruCache = new BitmapLruCache(6*1024*1024); + } if (sFallbackBitmap == null) { sFallbackBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.fallback_cover); } @@ -114,7 +122,6 @@ public class LazyCoverView extends ImageView /** * mHandler and mUiHandler callbacks */ - private static final int MSG_READ_COVER = 60; private static final int MSG_CREATE_COVER = 61; private static final int MSG_DRAW_COVER = 62; @@ -127,35 +134,22 @@ public class LazyCoverView extends ImageView } switch (message.what) { - case MSG_READ_COVER: { - // Cover was not in in-memory cache: Try to read from disk - Bitmap bitmap = sCoverCache.getStoredCover(payload.key); - if (bitmap != null) { - // Got it: promote to memory cache and let ui thread draw it - sCoverCache.cacheCover(payload.key, bitmap); - sUiHandler.sendMessage(sUiHandler.obtainMessage(MSG_DRAW_COVER, payload)); - } else { - sHandler.sendMessageDelayed(sHandler.obtainMessage(MSG_CREATE_COVER, payload), 80); - } - break; - } case MSG_CREATE_COVER: { // This message was sent due to a cache miss, but the cover might got cached in the meantime - Bitmap bitmap = sCoverCache.getCachedCover(payload.key); + Bitmap bitmap = sBitmapLruCache.get(payload.key); if (bitmap == null) { Song song = MediaUtils.getSongByTypeId(mContext.getContentResolver(), payload.key.mediaType, payload.key.mediaId); if (song != null) { // we got a song, try to fetch a cover - // will also populate all caches if a cover was found - bitmap = sCoverCache.getCoverFromSong(payload.key, song); + bitmap = song.getSmallCover(mContext); } if (bitmap == null) { - // song has no cover: return a failback one and store - // it (only) in memory + // song has no cover: return a failback bitmap = sFallbackBitmap; - sCoverCache.cacheCover(payload.key, bitmap); } } + // bitmap is non null: store in LRU cache and draw it + sBitmapLruCache.put(payload.key, bitmap); sUiHandler.sendMessage(sUiHandler.obtainMessage(MSG_DRAW_COVER, payload)); break; } @@ -181,8 +175,7 @@ public class LazyCoverView extends ImageView mExpectedKey = new CoverCache.CoverKey(type, id, CoverCache.SIZE_SMALL); if (drawFromCache(mExpectedKey, false) == false) { CoverMsg payload = new CoverMsg(mExpectedKey, this); - // We put the message at the queue start to out-race slow CREATE RPC's - sHandler.sendMessage(sHandler.obtainMessage(MSG_READ_COVER, payload)); + sHandler.sendMessage(sHandler.obtainMessage(MSG_CREATE_COVER, payload)); } } @@ -194,7 +187,7 @@ public class LazyCoverView extends ImageView */ public boolean drawFromCache(CoverCache.CoverKey key, boolean fadeIn) { boolean cacheHit = true; - Bitmap bitmap = sCoverCache.getCachedCover(key); + Bitmap bitmap = sBitmapLruCache.get(key); if (bitmap == null) { cacheHit = false; } @@ -213,4 +206,32 @@ public class LazyCoverView extends ImageView return cacheHit; } + + + /** + * A LRU cache implementation, using the CoverKey as key to store Bitmap objects + * + * Note that the implementation does not override create() in order to enable + * the use of fetch-if-cached functions: createBitmap() is therefore called + * by CoverCache itself. + */ + private static class BitmapLruCache extends LruCache { + /** + * Creates a new in-memory LRU cache + * + * @param size the lru cache size in bytes + */ + public BitmapLruCache(int size) { + super(size); + } + + /** + * Returns the cache size in bytes, not objects + */ + @Override + protected int sizeOf(CoverCache.CoverKey key, Bitmap value) { + return value.getByteCount(); + } + } + } diff --git a/src/ch/blinkenlights/android/vanilla/Song.java b/src/ch/blinkenlights/android/vanilla/Song.java index b569642f..1b2e7030 100644 --- a/src/ch/blinkenlights/android/vanilla/Song.java +++ b/src/ch/blinkenlights/android/vanilla/Song.java @@ -229,8 +229,7 @@ public class Song implements Comparable { if (sCoverCache == null) sCoverCache = new CoverCache(context.getApplicationContext()); - CoverCache.CoverKey key = new CoverCache.CoverKey(MediaUtils.TYPE_ALBUM, this.albumId, size); - Bitmap cover = sCoverCache.getCoverFromSong(key, this); + Bitmap cover = sCoverCache.getCoverFromSong(this, size); if (cover == null) flags |= FLAG_NO_COVER;