diff options
Diffstat (limited to 'src/com/android/tv/util/images/ImageCache.java')
-rw-r--r-- | src/com/android/tv/util/images/ImageCache.java | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/com/android/tv/util/images/ImageCache.java b/src/com/android/tv/util/images/ImageCache.java new file mode 100644 index 00000000..e260d67a --- /dev/null +++ b/src/com/android/tv/util/images/ImageCache.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2015 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.android.tv.util.images; + +import android.support.annotation.VisibleForTesting; +import android.util.Log; +import android.util.LruCache; +import com.android.tv.common.memory.MemoryManageable; +import com.android.tv.util.images.BitmapUtils.ScaledBitmapInfo; + +/** A convenience class for caching bitmap. */ +public class ImageCache implements MemoryManageable { + private static final float MAX_CACHE_SIZE_PERCENT = 0.8f; + private static final float MIN_CACHE_SIZE_PERCENT = 0.05f; + private static final float DEFAULT_CACHE_SIZE_PERCENT = 0.1f; + private static final boolean DEBUG = false; + private static final String TAG = "ImageCache"; + private static final int MIN_CACHE_SIZE_KBYTES = 1024; + + private final LruCache<String, ScaledBitmapInfo> mMemoryCache; + + /** + * Creates a new ImageCache object with a given cache size percent. + * + * @param memCacheSizePercent The cache size as a percent of available app memory. + */ + private ImageCache(float memCacheSizePercent) { + int memCacheSize = calculateMemCacheSize(memCacheSizePercent); + + // Set up memory cache + if (DEBUG) { + Log.d(TAG, "Memory cache created (size = " + memCacheSize + " Kbytes)"); + } + mMemoryCache = + new LruCache<String, ScaledBitmapInfo>(memCacheSize) { + /** + * Measure item size in kilobytes rather than units which is more practical for + * a bitmap cache + */ + @Override + protected int sizeOf(String key, ScaledBitmapInfo bitmapInfo) { + return (bitmapInfo.bitmap.getByteCount() + 1023) / 1024; + } + }; + } + + private static ImageCache sImageCache; + + /** + * Returns an existing ImageCache, if it doesn't exist, a new one is created using the supplied + * param. + * + * @param memCacheSizePercent The cache size as a percent of available app memory. Should be in + * range of MIN_CACHE_SIZE_PERCENT(0.05) ~ MAX_CACHE_SIZE_PERCENT(0.8). + * @return An existing retained ImageCache object or a new one if one did not exist + */ + public static synchronized ImageCache getInstance(float memCacheSizePercent) { + if (sImageCache == null) { + sImageCache = newInstance(memCacheSizePercent); + } + return sImageCache; + } + + @VisibleForTesting + static ImageCache newInstance(float memCacheSizePercent) { + return new ImageCache(memCacheSizePercent); + } + + /** + * Returns an existing ImageCache, if it doesn't exist, a new one is created using + * DEFAULT_CACHE_SIZE_PERCENT (0.1). + * + * @return An existing retained ImageCache object or a new one if one did not exist + */ + public static ImageCache getInstance() { + return getInstance(DEFAULT_CACHE_SIZE_PERCENT); + } + + /** + * Adds a bitmap to memory cache. + * + * <p>If there is an existing bitmap only replace it if {@link + * ScaledBitmapInfo#needToReload(ScaledBitmapInfo)} is true. + * + * @param bitmapInfo The {@link ScaledBitmapInfo} object to store + */ + public void putIfNeeded(ScaledBitmapInfo bitmapInfo) { + if (bitmapInfo == null || bitmapInfo.id == null) { + throw new IllegalArgumentException("Neither bitmap nor bitmap.id should be null."); + } + String key = bitmapInfo.id; + // Add to memory cache + synchronized (mMemoryCache) { + ScaledBitmapInfo old = mMemoryCache.put(key, bitmapInfo); + if (old != null && !old.needToReload(bitmapInfo)) { + mMemoryCache.put(key, old); + if (DEBUG) { + Log.d( + TAG, + "Kept original " + + old + + " in memory cache because it was larger than " + + bitmapInfo + + "."); + } + } else { + if (DEBUG) { + Log.d( + TAG, + "Add " + + bitmapInfo + + " to memory cache. Current size is " + + mMemoryCache.size() + + " / " + + mMemoryCache.maxSize() + + " Kbytes"); + } + } + } + } + + /** + * Get from memory cache. + * + * @param key Unique identifier for which item to get + * @return The bitmap if found in cache, null otherwise + */ + public ScaledBitmapInfo get(String key) { + ScaledBitmapInfo memBitmapInfo = mMemoryCache.get(key); + if (DEBUG) { + int hit = mMemoryCache.hitCount(); + int miss = mMemoryCache.missCount(); + String result = memBitmapInfo == null ? "miss" : "hit"; + double ratio = ((double) hit) / (hit + miss) * 100; + Log.d(TAG, "Memory cache " + result + " for " + key); + Log.d(TAG, "Memory cache " + hit + "h:" + miss + "m " + ratio + "%"); + } + return memBitmapInfo; + } + + /** + * Remove from memory cache. + * + * @param key Unique identifier for which item to remove + * @return The previous bitmap mapped by key + */ + public ScaledBitmapInfo remove(String key) { + return mMemoryCache.remove(key); + } + + /** + * Calculates the memory cache size based on a percentage of the max available VM memory. Eg. + * setting percent to 0.2 would set the memory cache to one fifth of the available memory. + * Throws {@link IllegalArgumentException} if percent is < 0.05 or > .8. memCacheSize is stored + * in kilobytes instead of bytes as this will eventually be passed to construct a LruCache which + * takes an int in its constructor. This value should be chosen carefully based on a number of + * factors Refer to the corresponding Android Training class for more discussion: + * http://developer.android.com/training/displaying-bitmaps/ + * + * @param percent Percent of available app memory to use to size memory cache. + */ + public static int calculateMemCacheSize(float percent) { + if (percent < MIN_CACHE_SIZE_PERCENT || percent > MAX_CACHE_SIZE_PERCENT) { + throw new IllegalArgumentException( + "setMemCacheSizePercent - percent must be " + + "between 0.05 and 0.8 (inclusive)"); + } + return Math.max( + MIN_CACHE_SIZE_KBYTES, + Math.round(percent * Runtime.getRuntime().maxMemory() / 1024)); + } + + @Override + public void performTrimMemory(int level) { + mMemoryCache.evictAll(); + } +} |