基础框架之LruDiskCache


import android.util.Base64;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Function;


public class LruDiskCache {
    private static final String TAG = "LruDiskCache";
    private static final String CACHE_ASE_KEY = "CACHE_ASE_KEY";
    final Map<String, Map<String, Entry>> mCacheMap;
    private String mCacheDirPath;
    private long mMaxSize;
    private long mMaxCount;
    private volatile long mCurrentSize;
    private volatile long mCurrentCount;

    private static LruDiskCache INSTANCE;

    public static void setup(String cacheDirPath, long maxSize, int maxCount) {
        INSTANCE = new LruDiskCache(cacheDirPath, maxSize, maxCount);
    }
    public static LruDiskCache getInstance() {
        return INSTANCE;
    }


    public LruDiskCache(String cacheDirPath, long maxSize, int maxCount) {
        mCacheDirPath = cacheDirPath;
        mMaxSize = maxSize;// 100M
        mMaxCount = maxCount;  // 2000 条缓存数据
        mCacheMap = Collections.synchronizedMap(new LinkedHashMap<String, Map<String, Entry>>(0, 0.75f, true));
        init();
    }

    private void init() {
        File rootDir = new File(mCacheDirPath);
        File[] list = rootDir.listFiles();
        for (File file : list) {
            String[] split = file.getName().split("_");
            Entry entry = new Entry(split[0], split[1], Integer.parseInt(split[2]));
            Map<String, Entry> map = mCacheMap.get(entry.key);
            if (map == null) {
                map = new HashMap<>();
                mCacheMap.put(entry.key, map);
            }
            mCurrentSize += file.length();
            mCurrentCount++;
            map.put(entry.language, entry);
        }
    }


    Observable<String> loadCache(final String key, final String language, final long time) {
        return Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                emitter.onNext(getCacheData(key, language, time));
                emitter.onComplete();
            }
        }).onErrorResumeNext(new Function<Throwable, ObservableSource<String>>() {
            @Override
            public ObservableSource<String> apply(@NonNull Throwable throwable) throws Exception {
                return Observable.empty();
            }
        });
    }

    public int getCacheVersion(String key, String language) {
        Map<String, Entry> map = mCacheMap.get(key);
        if (map != null) {
            Entry entry = map.get(language);
            if (entry != null) {
                return entry.version;
            }
        }
        return 0;
    }

    public String getCacheData(String key, String language, long time) throws IOException {
        Entry entry = null;
        if (time > 0) {
            entry = isExpiry(key, language, time);
        } else {
            Map<String, Entry> map = mCacheMap.get(key);
            if (map != null) {
                entry = map.get(language);
            }
        }
        if (entry != null) {
            byte[] data = readCacheData(entry);
            return decodeData(data);
        }
        return null;
    }

    public Entry isExpiry(String key, String language, long existTime) {
        Map<String, Entry> map = mCacheMap.get(key);
        if (map != null) {
            Entry entry = map.get(language);
            if (entry != null) {
                File file = entry.getFile();
                if (file.exists()) {
                    long time = System.currentTimeMillis() - file.lastModified();
                    if (time < existTime * 1000) {
                        return entry;
                    }
                    deleteCache(entry);
                }
            }
        }
        return null;
    }

    public void saveCacheData(String key, String language, int version, String content) throws IOException {
        synchronized (key) {
            Map<String, Entry> map = mCacheMap.get(key);
            if (map == null) {
                map = new HashMap<>();
                mCacheMap.put(key, map);
            }
            Entry entry = map.remove(language);
            if (entry != null) {
                deleteCache(entry);
            }
            entry = new Entry(key, language, version);
            byte[] bytes = encodeData(content);
            if ((mCurrentCount + 1 > mMaxCount) || (mCurrentSize + bytes.length > mMaxSize)) {
                cleanExpiredCache(key, bytes.length);
            }
            writeCacheData(entry, bytes);
            map.put(language, entry);
        }
    }

    private void cleanExpiredCache(String key, long length) {
        Iterator<Map.Entry<String, Map<String, Entry>>> iterator = mCacheMap.entrySet().iterator();
        do {
            if (!iterator.hasNext()) {
                break;
            }
            Map.Entry<String, Map<String, Entry>> next = iterator.next();
            if (!next.getKey().equals(key)) {
                iterator.remove();
                Map<String, Entry> map = next.getValue();
                for (Map.Entry<String, Entry> e : map.entrySet()) {
                    Entry entry = e.getValue();
                    if (entry != null) {
                        deleteCache(entry);
                    }
                }
                map.clear();
            }
        } while (mCurrentSize + length > mMaxSize);
    }


    public void deleteCache(String key, String language) {
        synchronized (key) {
            Map<String, Entry> map = mCacheMap.get(key);
            if (map != null) {
                Entry entry = map.remove(language);
                if (entry != null) {
                    deleteCache(entry);
                }
            }
        }
    }


    private byte[] readCacheData(Entry entry) throws IOException {
        synchronized (entry) {
            File file = entry.getFile();
            if (file.exists()) {
                FileInputStream ips = new FileInputStream(file);
                ByteArrayOutputStream ops = new ByteArrayOutputStream();
                byte[] buff = new byte[4096]; int len;
                while ((len = ips.read(buff)) != -1) {
                    ops.write(buff, 0, len);
                }
                ips.close();
                return ops.toByteArray();
            }
            return null;
        }
    }

    private void writeCacheData(Entry entry, byte[] data) throws IOException {
        synchronized (entry) {
            File file = entry.getFile();
            FileOutputStream ops = new FileOutputStream(file);
            ops.write(data);
            ops.flush();
            ops.close();
            mCurrentSize += file.length();
            mCurrentCount++;
        }
    }


    private void deleteCache(Entry entry) {
        synchronized (entry) {
            File file = entry.getFile();
            mCurrentSize -= file.length();
            mCurrentCount--;
            file.delete();
        }
    }


    private String decodeData(byte[] data) {
        try {
            byte[] rawKey = MessageDigest.getInstance("MD5").digest(CACHE_ASE_KEY.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] bytes = Base64.decode(data, Base64.NO_WRAP);
            return new String(cipher.doFinal(bytes), "UTF-8");
        } catch (Exception e) {
            Log.e(TAG, "decodeAES", e);
        }
        return null;
    }

    private byte[] encodeData(String content) {
        try {
            byte[] rawKey = MessageDigest.getInstance("MD5").digest(CACHE_ASE_KEY.getBytes("UTF-8"));
            SecretKeySpec skeySpec = new SecretKeySpec(rawKey, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            byte[] bytes = cipher.doFinal(content.getBytes("UTF-8"));
            return Base64.encode(bytes, Base64.NO_WRAP);
        } catch (Exception e) {
            Log.e(TAG, "encodeAES", e);
        }
        return null;
    }


    private final class Entry {
        private final String key;
        private final String language;
        private int version;

        public Entry(String key, String language, int version) {
            this.key = key;
            this.language = language;
            this.version = version;
        }

        public File getFile() {
            return  new File(mCacheDirPath, String.format("%s_%s_%s", key, language, version));
        }
    }



}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值