/******************************************************************************* * Copyright 2013 PAR Works, Inc * * 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.parworks.mars.cache; import android.app.ActivityManager; import android.content.Context; import android.graphics.Bitmap; import android.util.Log; import com.parworks.androidlibrary.utils.BitmapUtils; import com.parworks.mars.cache.BitmapWorkerTask.BitmapWorkerListener; /** * The memory cache used to store all the bitmaps. * * @author yusun */ public class BitmapCache { private static final String TAG = BitmapCache.class.getName(); /** Disk cache name */ private static final String DISK_CACHE_NAME = "mars"; /** Bitmap sample */ private static final int BITMAP_SAMPLE = 1; /** Disk cache size */ private static final int DISK_CACHE_SIZE = 1024 * 1024 * 256; //256MB /** Memory Cache */ private LruCache<String, Bitmap> mMemoryCache; /** Disk Cache */ private static DiskLruImageCache mImageDiskCache; /** The singleton instance holder */ private static BitmapCache INSTANCE; /** * Get the singleton instance of the bitmap cache * @return */ public static BitmapCache get() { return INSTANCE; } /** * Always trigger initialize first before any actions * * @param context */ public static void init(Context context) { if (INSTANCE == null) { INSTANCE = new BitmapCache(context); } // initialize disk cache mImageDiskCache = new DiskLruImageCache(context, DISK_CACHE_NAME, DISK_CACHE_SIZE, 100); Log.i(TAG, "Created disk cache at: " + mImageDiskCache.getCacheFolder().getAbsolutePath() + " with size: " + DISK_CACHE_SIZE); } private BitmapCache(Context context) { // Get memory class of this device, exceeding this amount // will throw an OutOfMemory exception final int memClass = ((ActivityManager) context.getSystemService( Context.ACTIVITY_SERVICE)).getMemoryClass(); // Use 1/4th of the available memory for this memory cache final int cacheSize = 1024 * 1024 * memClass / 4; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // The cache size will be measured in bytes rather than number of items. return bitmap.getRowBytes() * bitmap.getHeight(); } }; Log.i(TAG, "Created mem cache with size: " + cacheSize); } /** * Get the bitmap from cache system. * <p> * The key is first looked in mem cache, if not found, * disk cache will be looked. If still not found, null * will be returned. * <p> * Users can trigger {@link #downloadImage(String, String)} * to download the bitmap with a given URL. * * @param key the bitmap key to look for * @return the bitmap */ public Bitmap getBitmap(String key) { // check mem cache first Bitmap bitmap = mMemoryCache.get(key); if (bitmap == null) { // check disk cache if not present in mem cache bitmap = mImageDiskCache.getBitmap(key, BITMAP_SAMPLE); if (bitmap != null) { mMemoryCache.put(key, bitmap); } } return bitmap; } public void getBitmapAsync(final String url, final BitmapWorkerListener onCompleteListener) { String key = getImageKeyFromURL(url); Bitmap bitmap = getBitmap(key); if (bitmap == null) { new BitmapWorkerTask(url, new BitmapWorkerListener() { @Override public void bitmapLoaded(Bitmap bitmap) { if (onCompleteListener != null) { onCompleteListener.bitmapLoaded(bitmap); } } }).execute(); } else { if (onCompleteListener != null) { onCompleteListener.bitmapLoaded(bitmap); } } } public boolean containsKey(String key) { return mImageDiskCache.containsKey(key); } /** * Download the image from web with the given URL. Once it * is downloaded, the bitmap will be automatically put into * both mem and disk caches. * <p> * Note, this method should be called in the background thread. * * @param key * @param url * @return bitmap */ public Bitmap downloadImage(String url) { Bitmap bitmap = null; try { // download the image BitmapUtils bitmapUtils = new BitmapUtils(); bitmap = bitmapUtils.getBitmap(url, BITMAP_SAMPLE); String key = getImageKeyFromURL(url); // put into disk cache mImageDiskCache.put(key, bitmap); // put into mem cache mMemoryCache.put(key, bitmap); Log.d(TAG, "Finished downloading link for " + url); } catch (Exception e) { Log.w(TAG, "Failed to download the image: " + url + " : " + e.getMessage()); } return bitmap; } public void clearCache() { this.mMemoryCache.evictAll(); } public static String getImageKeyFromURL(String imageUrl) { String key = Integer.toString(imageUrl.hashCode()); key = key.replaceAll("-", "_"); return key; } }