Android画像キャッシュ強化版(LruCache+DiskLruCache+ソフトリファレンス)


LruCache+DiskLruCache+ソフトリファレンスの3つのキャッシュ技術を組み合わせて、画像のキャッシュ効果を強化します.
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Environment;
import android.support.v4.util.LruCache;

import com.jakewharton.disklrucache.DiskLruCache;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.SoftReference;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.LinkedHashMap;

public class ImageCache {
    private static final String IMAGE_PATH = "image";
    private static final int DEFAULT_MEM_CACHE_SIZE = 1024 * 1024 * 8;
    private static final int DEFAULT_DIS_CACHE_SIZE = 1024 * 1024 * 10;
    private static final int DEDAULT_SOFT_CACHE_NUM = 20;

    private LruCache<String, Bitmap> mLruCache;
    private DiskLruCache mDisLruCache;
    private LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;

    private volatile static ImageCache mImageCache;

    private ImageCache(Context context) {
        mLruCache = new LruCache<String,Bitmap>(DEFAULT_MEM_CACHE_SIZE) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return getBitmapSize(value);
            }

            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                if (evicted) {
                    mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
                }
                super.entryRemoved(evicted, key, oldValue, newValue);
            }
        };

        try {
            mDisLruCache.open(getDiskCacheDir(context), getAppVersion(context), 1, DEFAULT_DIS_CACHE_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }


        mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(
                DEDAULT_SOFT_CACHE_NUM, 0.75f, true) {
            private static final long serialVersionUID = 1L;
            /** *         20   ,                   */
            @Override
            protected boolean removeEldestEntry(
                    Entry<String, SoftReference<Bitmap>> eldest) {
                if (size() > DEDAULT_SOFT_CACHE_NUM) {
                    return true;
                }
                return false;
            }
        };

    }

    public static ImageCache getInstance(Context context) {
        if (mImageCache == null) {
            synchronized (ImageCache.class) {
                if (mImageCache == null) {
                    mImageCache = new ImageCache(context);
                }
            }
        }
        return mImageCache;
    }

    /* *         key   , *                  ,          * *   :   mLruCache         ,             *    mSoftCache    ,   mLruCache                *     Disk          * * */
    public Bitmap get(String key) {
        key = hashKey(key);

        DiskLruCache.Snapshot snapshot = null;
        Bitmap bitmap = mLruCache.get(key);

        if (bitmap == null) {
            bitmap = mSoftCache.get(key).get();

            if (bitmap == null) {
                if (mDisLruCache != null) {
                    try {
                        snapshot = mDisLruCache.get(key);

                        if (snapshot != null) {
                            InputStream is = snapshot.getInputStream(0);
                            bitmap = BitmapFactory.decodeStream(is);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                mSoftCache.remove(key);
            }

            if (bitmap != null) {
                mLruCache.put(key, bitmap);
            }
        }

        return bitmap;
    }

    /* *             ,            , * */
    public void put(String key, Bitmap bitmap) {
        key = hashKey(key);

        mLruCache.put(key, bitmap);

        if (mDisLruCache != null) {
            try {
                DiskLruCache.Editor editor = mDisLruCache.edit(key);

                if (editor != null) {
                    OutputStream outputStream = editor.newOutputStream(0);
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
                    outputStream.write(byteArrayOutputStream.toByteArray());
                    editor.commit();
                    mDisLruCache.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /* *        * */
    private int getBitmapSize(Bitmap bitmap) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
            return bitmap.getByteCount();
        }
        return bitmap.getRowBytes() * bitmap.getHeight();
    }

    /* *   Disk     * */
    private File getDiskCacheDir(Context context) {
        String cachePath;
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }

        return new File(cachePath + File.separator + IMAGE_PATH);
    }

    /* *         * */
    private int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(),0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }

    /* *  key   MD5 * */
    private String hashKey(String key) {
        String cacheKey;

        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance("MD5");
            digest.update(key.getBytes());
            cacheKey = bytesToHexString(digest.digest());
        } catch (NoSuchAlgorithmException e) {
            cacheKey = String.valueOf(key.hashCode());
            e.printStackTrace();
        }
        return cacheKey;

    }

    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(0xFF & bytes[i]);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
        }
        return sb.toString();
    }
}

参考記事:Android画像キャッシュ技術
Androidの画像の3級cacheポリシー(メモリ、ファイル、ネットワーク)の2:メモリキャッシュポリシー