/* * Copyright (C) 2011 The Android Open Source Project * * 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 com.squareup.picasso; import android.content.Context; import android.graphics.Bitmap; import java.util.LinkedHashMap; import java.util.Map; /** A memory cache which uses a least-recently used eviction policy. */ public class LruCache implements Cache { final LinkedHashMap map; private final int maxSize; private int size; private int putCount; private int evictionCount; private int hitCount; private int missCount; /** Create a cache using an appropriate portion of the available RAM as the maximum size. */ public LruCache(Context context) { this(Utils.calculateMemoryCacheSize(context)); } /** Create a cache with a given maximum size in bytes. */ public LruCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("Max size must be positive."); } this.maxSize = maxSize; this.map = new LinkedHashMap(0, 0.75f, true); } @Override public Bitmap get(String key) { if (key == null) { throw new NullPointerException("key == null"); } Bitmap mapValue; synchronized (this) { mapValue = map.get(key); if (mapValue != null) { hitCount++; return mapValue; } missCount++; } return null; } @Override public void set(String key, Bitmap bitmap) { if (key == null || bitmap == null) { throw new NullPointerException("key == null || bitmap == null"); } Bitmap previous; synchronized (this) { putCount++; size += Utils.getBitmapBytes(bitmap); previous = map.put(key, bitmap); if (previous != null) { size -= Utils.getBitmapBytes(previous); } } trimToSize(maxSize); } private void trimToSize(int maxSize) { while (true) { String key; Bitmap value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException( getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if (size <= maxSize || map.isEmpty()) { break; } Map.Entry toEvict = map.entrySet().iterator().next(); key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= Utils.getBitmapBytes(value); evictionCount++; } } } /** Clear the cache. */ public final void evictAll() { trimToSize(-1); // -1 will evict 0-sized elements } /** Returns the sum of the sizes of the entries in this cache. */ public final synchronized int size() { return size; } /** Returns the maximum sum of the sizes of the entries in this cache. */ public final synchronized int maxSize() { return maxSize; } public final synchronized void clear() { evictAll(); } /** Returns the number of times {@link #get} returned a value. */ public final synchronized int hitCount() { return hitCount; } /** Returns the number of times {@link #get} returned {@code null}. */ public final synchronized int missCount() { return missCount; } /** Returns the number of times {@link #set(String, Bitmap)} was called. */ public final synchronized int putCount() { return putCount; } /** Returns the number of values that have been evicted. */ public final synchronized int evictionCount() { return evictionCount; } }