package org.mozilla.osmdroid.tileprovider;
import android.graphics.drawable.Drawable;
import org.mozilla.mozstumbler.service.AppGlobals;
import org.mozilla.mozstumbler.service.core.logging.ClientLog;
import org.mozilla.mozstumbler.svclocator.services.log.LoggerUtil;
import java.util.LinkedHashMap;
/*
* This is an LRUCache to hold onto Drawable instances for TileOverlay instances.
*
* Note that when tiles are evicted from cache, we attempt to push them back into
* the BitmapPool if they are recyclable.
*
* It is important to note that this class is tightly coupled to the BitmapPool to cache
* Bitmap instances.
*
* The ensureCapacity method grows the LRUMapTileCache to hold onto instances of Bitmaps, but
* those bitmaps are recycled into the BitmapPool.
*
* If the LRUMapTileCache evicts tiles very quickly, the eldest tiles are pushed immediately back
* into the BitmapPool.
*
* The code in BitmapPool should really be merged into this class.
*
*/
public class LRUMapTileCache {
private static final String LOG_TAG = LoggerUtil.makeLogTag(LRUMapTileCache.class);
private int mCapacity = 0;
private InnerLRUMapTileCache innerCache;
public LRUMapTileCache(int capacity) {
ensureCapacity(capacity);
}
public synchronized void ensureCapacity(final int aCapacity) {
if (aCapacity > mCapacity) {
ClientLog.d(LOG_TAG, "Tile cache increased from " + mCapacity + " to " + aCapacity);
mCapacity = aCapacity;
innerCache = new InnerLRUMapTileCache(mCapacity);
System.gc();
}
}
public void clear() {
innerCache.clear();
}
public boolean containsKey(MapTile key) {
return innerCache.containsKey(key);
}
public Drawable get(MapTile key) {
return innerCache.get(key);
}
public void put(MapTile key, Drawable value) {
innerCache.put(key, value);
}
private class InnerLRUMapTileCache extends LinkedHashMap<MapTile, Drawable> {
// This load factor is the same as what is defined in java.util.HashMap
final static float DEFAULT_LOAD_FACTOR = (float) 0.75;
final static boolean LRU_USES_ACCESS_ORDER = true;
private static final long serialVersionUID = -541142277575493335L;
public InnerLRUMapTileCache(final int aCapacity) {
// The default implementation of HashMap defines
// Tile eviction happens at approximately 0.75 * total capacity,
// so doubling the capacity of the underlying LinkedHashMap should hold onto tiles
// long enough that we are not constantly constantly pushing tiles out into the
// BitmapPool.
super(aCapacity * 2, DEFAULT_LOAD_FACTOR, LRU_USES_ACCESS_ORDER);
}
@Override
public Drawable remove(final Object aKey) {
final Drawable drawable = super.remove(aKey);
// @TODO: vng is there ever a case where this is not true?
// BitmapTileSourceBase seems like the only place where drawables are created.
// This seems like a case where all the interfaces pass around the super class of Drawable,
// but in reality, we're always actually using ReusableBitmapDrawable instances.
if (drawable instanceof ReusableBitmapDrawable) {
BitmapPool.getInstance().returnDrawableToPool((ReusableBitmapDrawable) drawable);
}
return drawable;
}
@Override
public void clear() {
// remove them all individually so that they get recycled
while (!isEmpty()) {
remove(keySet().iterator().next());
}
// and then clear
super.clear();
}
@Override
protected boolean removeEldestEntry(final java.util.Map.Entry<MapTile, Drawable> aEldest) {
if (size() > mCapacity) {
final MapTile eldest = aEldest.getKey();
if (AppGlobals.isDebug) {
ClientLog.d(LOG_TAG, "Remove old tile: " + eldest);
}
remove(eldest);
// don't return true because we've already removed it
}
return false;
}
}
}