Refactor bitmap cache into a discrete class
This commit is contained in:
parent
4a120630b4
commit
a85848c246
179
src/org/kreed/vanilla/Cache.java
Normal file
179
src/org/kreed/vanilla/Cache.java
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Christopher Eby <kreed@kreed.org>
|
||||
*
|
||||
* This file is part of Vanilla Music Player.
|
||||
*
|
||||
* Vanilla Music Player is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Vanilla Music Player is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.kreed.vanilla;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A key/value map that discards old items. When the capacity of the cache is
|
||||
* reached, the oldest item (by insertion time) will be discarded.
|
||||
*
|
||||
* Keys should be non-negative.
|
||||
*
|
||||
* @param <E> The type of the values. (Keys will be longs).
|
||||
*/
|
||||
public class Cache<E> {
|
||||
/**
|
||||
* The keys contained in the cache, stored in the order of insertion.
|
||||
*/
|
||||
private long[] mKeys;
|
||||
/**
|
||||
* The values contained in the cache, stored in a location corresponding
|
||||
* to the keys in mKeys.
|
||||
*/
|
||||
private Object[] mValues;
|
||||
|
||||
/**
|
||||
* Create a Cache.
|
||||
*
|
||||
* @param capacity The capacity of the cache. This is fixed and may not be
|
||||
* changed after construction.
|
||||
*/
|
||||
public Cache(int capacity)
|
||||
{
|
||||
mKeys = new long[capacity];
|
||||
mValues = new Object[capacity];
|
||||
Arrays.fill(mKeys, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the number of items in the cache.
|
||||
*
|
||||
* @return The number of items in the cache.
|
||||
*/
|
||||
public int count()
|
||||
{
|
||||
long[] keys = mKeys;
|
||||
int count = keys.length;
|
||||
while (--count != -1 && keys[count] == -1);
|
||||
return count + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the index of the given key.
|
||||
*
|
||||
* @param key The key to search for.
|
||||
* @return The index, or -1 if the key was not found.
|
||||
*/
|
||||
private int indexOf(long key)
|
||||
{
|
||||
long[] keys = mKeys;
|
||||
for (int i = keys.length; --i != -1; )
|
||||
if (keys[i] == key)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the value with the given key.
|
||||
*
|
||||
* @param key The key to search with.
|
||||
* @return The value, or null if the given key is not contained in this
|
||||
* cache.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public E get(long key)
|
||||
{
|
||||
if (key < 0)
|
||||
throw new IllegalArgumentException("Keys must be non-negative.");
|
||||
|
||||
int i = indexOf(key);
|
||||
return i == -1 ? null : (E)mValues[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the age of the item with the given key so that it will be
|
||||
* discarded last.
|
||||
*
|
||||
* @param key The key of the item to touch.
|
||||
*/
|
||||
public void touch(long key)
|
||||
{
|
||||
if (key < 0)
|
||||
throw new IllegalArgumentException("Keys must be non-negative.");
|
||||
|
||||
long[] keys = mKeys;
|
||||
Object[] values = mValues;
|
||||
|
||||
int oldPos = indexOf(key);
|
||||
int newPos = count() - 1;
|
||||
|
||||
if (oldPos != newPos && oldPos != -1) {
|
||||
Object value = values[oldPos];
|
||||
System.arraycopy(keys, oldPos + 1, keys, oldPos, newPos - oldPos);
|
||||
System.arraycopy(values, oldPos + 1, values, oldPos, newPos - oldPos);
|
||||
keys[newPos] = key;
|
||||
values[newPos] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Discard the oldest item in the cache. Does nothing if the cache is not
|
||||
* full.
|
||||
*
|
||||
* @return The item that was discarded, or null if the cache is not full.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public E discardOldest()
|
||||
{
|
||||
int count = count();
|
||||
// Cache is not full.
|
||||
if (count != mKeys.length)
|
||||
return null;
|
||||
|
||||
E removed = (E)mValues[0];
|
||||
System.arraycopy(mKeys, 1, mKeys, 0, count - 1);
|
||||
System.arraycopy(mValues, 1, mValues, 0, count - 1);
|
||||
mKeys[mKeys.length - 1] = -1;
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an item into the cache. If the cache is full, the oldest item
|
||||
* will be discarded.
|
||||
*
|
||||
* @param key The key to place the item at. Must be a duplicate of an
|
||||
* existing key in the cache.
|
||||
* @param value The item.
|
||||
* @return The discarded item, or null if no items were discarded.
|
||||
*/
|
||||
public E put(long key, E value)
|
||||
{
|
||||
if (key < 0)
|
||||
throw new IllegalArgumentException("Keys must be non-negative.");
|
||||
|
||||
E discarded = discardOldest();
|
||||
int count = count();
|
||||
mKeys[count] = key;
|
||||
mValues[count] = value;
|
||||
return discarded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the keys and return the values to be cleared by the caller.
|
||||
*
|
||||
* @return The values in the cache, untouched.
|
||||
*/
|
||||
public Object[] clear()
|
||||
{
|
||||
Arrays.fill(mKeys, -1);
|
||||
return mValues;
|
||||
}
|
||||
}
|
@ -29,7 +29,6 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.View;
|
||||
@ -62,12 +61,7 @@ public final class CoverView extends View implements Handler.Callback {
|
||||
/**
|
||||
* Cache of cover bitmaps generated for songs. The song ids are the keys.
|
||||
*/
|
||||
private SparseArray<Bitmap> mBitmapCache = new SparseArray<Bitmap>();
|
||||
/**
|
||||
* Stores the order of insertion of songs ids into the cache. This allows
|
||||
* for old bitmaps to be discarded.
|
||||
*/
|
||||
private int[] mCacheTimeline = new int[8];
|
||||
private Cache<Bitmap> mBitmapCache = new Cache<Bitmap>(8);
|
||||
|
||||
private int mTimelinePos;
|
||||
private Scroller mScroller;
|
||||
@ -147,7 +141,12 @@ public final class CoverView extends View implements Handler.Callback {
|
||||
*/
|
||||
private void regenerateBitmaps()
|
||||
{
|
||||
mBitmapCache.clear();
|
||||
Object[] bitmaps = mBitmapCache.clear();
|
||||
for (int i = bitmaps.length; --i != -1; ) {
|
||||
if (bitmaps[i] != null)
|
||||
((Bitmap)bitmaps[i]).recycle();
|
||||
bitmaps[i] = null;
|
||||
}
|
||||
for (int i = STORE_SIZE; --i != -1; )
|
||||
setSong(i, mSongs[i]);
|
||||
}
|
||||
@ -315,51 +314,17 @@ public final class CoverView extends View implements Handler.Callback {
|
||||
*/
|
||||
private void generateBitmap(Song song)
|
||||
{
|
||||
int id = (int)song.id;
|
||||
boolean created = false;
|
||||
|
||||
if (mBitmapCache.get(id) == null) {
|
||||
created = true;
|
||||
|
||||
Bitmap bitmap;
|
||||
Bitmap bitmap = mBitmapCache.get(song.id);
|
||||
if (bitmap == null) {
|
||||
if (mSeparateInfo)
|
||||
bitmap = CoverBitmap.createSeparatedBitmap(song, getWidth(), getHeight());
|
||||
else
|
||||
bitmap = CoverBitmap.createOverlappingBitmap(song, getWidth(), getHeight());
|
||||
mBitmapCache.put(id, bitmap);
|
||||
|
||||
mBitmapCache.put(song.id, bitmap);
|
||||
postInvalidate();
|
||||
} else {
|
||||
mBitmapCache.touch(song.id);
|
||||
}
|
||||
|
||||
int[] timeline = mCacheTimeline;
|
||||
int end = timeline.length;
|
||||
while (end > 0 && timeline[end - 1] == 0)
|
||||
--end;
|
||||
|
||||
if (!created) {
|
||||
// If we already have a bitmap for the given song, erase the old
|
||||
// id and make room for it at the end of the timeline.
|
||||
int i = end;
|
||||
while (--i != -1 && timeline[i] != id);
|
||||
if (i != -1) {
|
||||
System.arraycopy(timeline, i + 1, timeline, i, timeline.length - i - 1);
|
||||
--end;
|
||||
}
|
||||
}
|
||||
|
||||
if (end == timeline.length) {
|
||||
// If the timeline is full, erase the bitmap for the oldest song
|
||||
// and make room for the new bitmap at the end of the timeline.
|
||||
int toRemove = timeline[0];
|
||||
System.arraycopy(timeline, 1, timeline, 0, timeline.length - 1);
|
||||
Bitmap bitmap = mBitmapCache.get(toRemove);
|
||||
mBitmapCache.remove(toRemove);
|
||||
if (bitmap != null)
|
||||
bitmap.recycle();
|
||||
--end;
|
||||
}
|
||||
|
||||
timeline[end] = id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user