aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/util/images/ImageCache.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/util/images/ImageCache.java')
-rw-r--r--src/com/android/tv/util/images/ImageCache.java191
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();
+ }
+}