Move in-memory LRU cache from CoverCache into LazyCoverView
This commit is contained in:
parent
26a59e0458
commit
1fa2827744
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user