Move in-memory LRU cache from CoverCache into LazyCoverView

This commit is contained in:
Adrian Ulrich 2015-12-23 16:03:40 +01:00
parent 26a59e0458
commit 1fa2827744
3 changed files with 59 additions and 113 deletions

View File

@ -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<CoverKey, Bitmap> {
/**
* 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;

View File

@ -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<CoverCache.CoverKey, Bitmap> {
/**
* 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();
}
}
}

View File

@ -229,8 +229,7 @@ public class Song implements Comparable<Song> {
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;