michael@0: /* michael@0: * Copyright (C) 2011 The Android Open Source Project michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: package com.squareup.picasso; michael@0: michael@0: import android.content.Context; michael@0: import android.graphics.Bitmap; michael@0: import java.util.LinkedHashMap; michael@0: import java.util.Map; michael@0: michael@0: /** A memory cache which uses a least-recently used eviction policy. */ michael@0: public class LruCache implements Cache { michael@0: final LinkedHashMap map; michael@0: private final int maxSize; michael@0: michael@0: private int size; michael@0: private int putCount; michael@0: private int evictionCount; michael@0: private int hitCount; michael@0: private int missCount; michael@0: michael@0: /** Create a cache using an appropriate portion of the available RAM as the maximum size. */ michael@0: public LruCache(Context context) { michael@0: this(Utils.calculateMemoryCacheSize(context)); michael@0: } michael@0: michael@0: /** Create a cache with a given maximum size in bytes. */ michael@0: public LruCache(int maxSize) { michael@0: if (maxSize <= 0) { michael@0: throw new IllegalArgumentException("Max size must be positive."); michael@0: } michael@0: this.maxSize = maxSize; michael@0: this.map = new LinkedHashMap(0, 0.75f, true); michael@0: } michael@0: michael@0: @Override public Bitmap get(String key) { michael@0: if (key == null) { michael@0: throw new NullPointerException("key == null"); michael@0: } michael@0: michael@0: Bitmap mapValue; michael@0: synchronized (this) { michael@0: mapValue = map.get(key); michael@0: if (mapValue != null) { michael@0: hitCount++; michael@0: return mapValue; michael@0: } michael@0: missCount++; michael@0: } michael@0: michael@0: return null; michael@0: } michael@0: michael@0: @Override public void set(String key, Bitmap bitmap) { michael@0: if (key == null || bitmap == null) { michael@0: throw new NullPointerException("key == null || bitmap == null"); michael@0: } michael@0: michael@0: Bitmap previous; michael@0: synchronized (this) { michael@0: putCount++; michael@0: size += Utils.getBitmapBytes(bitmap); michael@0: previous = map.put(key, bitmap); michael@0: if (previous != null) { michael@0: size -= Utils.getBitmapBytes(previous); michael@0: } michael@0: } michael@0: michael@0: trimToSize(maxSize); michael@0: } michael@0: michael@0: private void trimToSize(int maxSize) { michael@0: while (true) { michael@0: String key; michael@0: Bitmap value; michael@0: synchronized (this) { michael@0: if (size < 0 || (map.isEmpty() && size != 0)) { michael@0: throw new IllegalStateException( michael@0: getClass().getName() + ".sizeOf() is reporting inconsistent results!"); michael@0: } michael@0: michael@0: if (size <= maxSize || map.isEmpty()) { michael@0: break; michael@0: } michael@0: michael@0: Map.Entry toEvict = map.entrySet().iterator().next(); michael@0: key = toEvict.getKey(); michael@0: value = toEvict.getValue(); michael@0: map.remove(key); michael@0: size -= Utils.getBitmapBytes(value); michael@0: evictionCount++; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** Clear the cache. */ michael@0: public final void evictAll() { michael@0: trimToSize(-1); // -1 will evict 0-sized elements michael@0: } michael@0: michael@0: /** Returns the sum of the sizes of the entries in this cache. */ michael@0: public final synchronized int size() { michael@0: return size; michael@0: } michael@0: michael@0: /** Returns the maximum sum of the sizes of the entries in this cache. */ michael@0: public final synchronized int maxSize() { michael@0: return maxSize; michael@0: } michael@0: michael@0: public final synchronized void clear() { michael@0: evictAll(); michael@0: } michael@0: michael@0: /** Returns the number of times {@link #get} returned a value. */ michael@0: public final synchronized int hitCount() { michael@0: return hitCount; michael@0: } michael@0: michael@0: /** Returns the number of times {@link #get} returned {@code null}. */ michael@0: public final synchronized int missCount() { michael@0: return missCount; michael@0: } michael@0: michael@0: /** Returns the number of times {@link #set(String, Bitmap)} was called. */ michael@0: public final synchronized int putCount() { michael@0: return putCount; michael@0: } michael@0: michael@0: /** Returns the number of values that have been evicted. */ michael@0: public final synchronized int evictionCount() { michael@0: return evictionCount; michael@0: } michael@0: }