/*
* Copyright (C) 2013 Peng fei Pan <sky@xiaopan.me>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.xiaopan.sketch.cache;
import android.content.Context;
import android.text.format.Formatter;
import me.xiaopan.sketch.SLogType;
import me.xiaopan.sketch.SLog;
import me.xiaopan.sketch.drawable.RefBitmap;
import me.xiaopan.sketch.util.LruCache;
import me.xiaopan.sketch.util.SketchUtils;
public class LruMemoryCache implements MemoryCache {
private final LruCache<String, RefBitmap> cache;
protected String logName = "LruMemoryCache";
private Context context;
private boolean closed;
private boolean disabled;
public LruMemoryCache(Context context, int maxSize) {
context = context.getApplicationContext();
this.context = context;
this.cache = new RefBitmapLruCache(this, maxSize);
}
@Override
public synchronized void put(String key, RefBitmap refBitmap) {
if (closed) {
return;
}
if (disabled) {
SLog.w(SLogType.CACHE, logName, "Disabled. Unable put, key=%s", key);
return;
}
if (cache.get(key) != null) {
SLog.w(SLogType.CACHE, logName, String.format("Exist. key=%s", key));
return;
}
int oldCacheSize = 0;
if (SLogType.CACHE.isEnabled()) {
oldCacheSize = cache.size();
}
cache.put(key, refBitmap);
SLog.i(SLogType.CACHE, logName, "put. beforeCacheSize=%s. %s. afterCacheSize=%s",
Formatter.formatFileSize(context, oldCacheSize), refBitmap.getInfo(),
Formatter.formatFileSize(context, cache.size()));
}
@Override
public synchronized RefBitmap get(String key) {
if (closed) {
return null;
}
if (disabled) {
SLog.w(SLogType.CACHE, logName, "Disabled. Unable get, key=%s", key);
return null;
}
return cache.get(key);
}
@Override
public synchronized RefBitmap remove(String key) {
if (closed) {
return null;
}
if (disabled) {
SLog.w(SLogType.CACHE, logName, "Disabled. Unable remove, key=%s", key);
return null;
}
RefBitmap refBitmap = cache.remove(key);
SLog.i(SLogType.CACHE, logName, "remove. memoryCacheSize: %s",
Formatter.formatFileSize(context, cache.size()));
return refBitmap;
}
@Override
public synchronized long getSize() {
if (closed) {
return 0;
}
return cache.size();
}
@Override
public long getMaxSize() {
return cache.maxSize();
}
@Override
public synchronized void trimMemory(int level) {
if (closed) {
return;
}
long memoryCacheSize = getSize();
if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
cache.evictAll();
} else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
cache.trimToSize(cache.maxSize() / 2);
}
long releasedSize = memoryCacheSize - getSize();
SLog.w(SLogType.CACHE, logName, "trimMemory. level=%s, released: %s",
SketchUtils.getTrimLevelName(level), Formatter.formatFileSize(context, releasedSize));
}
@Override
public boolean isDisabled() {
return disabled;
}
@Override
public void setDisabled(boolean disabled) {
this.disabled = disabled;
if (disabled) {
SLog.w(SLogType.CACHE, logName, "setDisabled. %s", true);
} else {
SLog.i(SLogType.CACHE, logName, "setDisabled. %s", false);
}
}
@Override
public synchronized void clear() {
if (closed) {
return;
}
SLog.w(SLogType.CACHE, logName, "clear. before size: %s",
Formatter.formatFileSize(context, cache.size()));
cache.evictAll();
}
@Override
public synchronized boolean isClosed() {
return closed;
}
@Override
public synchronized void close() {
if (closed) {
return;
}
closed = true;
cache.evictAll();
}
@Override
public String getKey() {
return String.format("%s(maxSize=%s)", logName, Formatter.formatFileSize(context, getMaxSize()));
}
private static class RefBitmapLruCache extends LruCache<String, RefBitmap> {
private LruMemoryCache cache;
public RefBitmapLruCache(LruMemoryCache cache, int maxSize) {
super(maxSize);
this.cache = cache;
}
@Override
public RefBitmap put(String key, RefBitmap refBitmap) {
refBitmap.setIsCached(cache.logName + ":put", true);
return super.put(key, refBitmap);
}
@Override
public int sizeOf(String key, RefBitmap refBitmap) {
int bitmapSize = refBitmap.getByteCount();
return bitmapSize == 0 ? 1 : bitmapSize;
}
@Override
protected void entryRemoved(boolean evicted, String key, RefBitmap oldRefBitmap, RefBitmap newRefBitmap) {
oldRefBitmap.setIsCached(cache.logName + ":entryRemoved", false);
}
}
}