package com.arialyy.frame.cache;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.util.LruCache;
import android.text.TextUtils;
import com.arialyy.frame.util.AndroidUtils;
import com.arialyy.frame.util.AppUtils;
import com.arialyy.frame.util.FileUtil;
import com.arialyy.frame.util.StreamUtil;
import com.arialyy.frame.util.StringUtil;
import com.arialyy.frame.util.show.FL;
import com.arialyy.frame.util.show.L;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by Lyy on 2015/4/9.
* 缓存抽象类,封装了缓存的读写操作
*/
public abstract class AbsCache implements CacheParam {
private static final String TAG = "AbsCache";
/**
* 磁盘缓存工具
*/
private DiskLruCache mDiskLruCache = null;
/**
* 内存缓存工具
*/
private LruCache<String, byte[]> mMemoryCache = null;
/**
* 是否使用内存缓存
*/
private boolean useMemory = false;
private int mMaxMemory;
private Context mContext;
private static final Object mDiskCacheLock = new Object();
/**
* 默认使用默认路径
*
* @param context
* @param useMemory 是否使用内存缓存
*/
protected AbsCache(Context context, boolean useMemory) {
this.mContext = context;
this.useMemory = useMemory;
init(DEFAULT_DIR, 1, SMALL_DISK_CACHE_CAPACITY);
}
/**
* 指定缓存文件夹
*
* @param context
* @param useMemory 是否使用内存缓存
* @param cacheDir 缓存文件夹
*/
protected AbsCache(Context context, boolean useMemory, @NonNull String cacheDir) {
this.mContext = context;
this.useMemory = useMemory;
init(cacheDir, 1, SMALL_DISK_CACHE_CAPACITY);
}
private void init(String cacheDir, int valueCount, long cacheSize) {
initDiskCache(cacheDir, valueCount, cacheSize);
initMemoryCache();
}
/**
* 初始化磁盘缓存
*/
protected void initDiskCache(String cacheDir, int valueCount, long cacheSize) {
try {
File dir = getDiskCacheDir(mContext, cacheDir);
if (!dir.exists()) {
dir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(dir, AppUtils.getVersionNumber(mContext), valueCount, cacheSize);
} catch (IOException e) {
FL.e(this, "createCacheFailed\n" + FL.getExceptionString(e));
}
}
/**
* 初始化内存缓存
*/
protected void initMemoryCache() {
if (!useMemory)
return;
// 获取应用程序最大可用内存
mMaxMemory = (int) Runtime.getRuntime().maxMemory();
// 设置图片缓存大小为程序最大可用内存的1/8
mMemoryCache = new LruCache<>(mMaxMemory / 8);
}
/**
* 是否使用内存缓存
*
* @param useMemory
*/
protected void setUseMemory(boolean useMemory) {
this.useMemory = useMemory;
initMemoryCache();
}
/**
* 设置内存缓存大小
*
* @param size
*/
protected void setMemoryCache(int size) {
mMemoryCache.resize(size);
}
/**
* 打开某个目录下的缓存
*
* @param cacheDir 缓存目录,只需填写文件夹名,不需要写路径
* @param valueCount 指定同一个key可以对应多少个缓存文件,基本都是传1
* @param cacheSize 缓存大小
* @see CacheParam
*/
protected void openDiskCache(@NonNull String cacheDir, int valueCount, long cacheSize) {
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null && mDiskLruCache.isClosed()) {
try {
File dir = getDiskCacheDir(mContext, cacheDir);
if (!dir.exists()) {
dir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(dir, AppUtils.getVersionNumber(mContext), valueCount, cacheSize);
} catch (IOException e) {
FL.e(this, "createCacheFailed\n" + FL.getExceptionString(e));
}
}
}
}
/**
* 把缓存写入磁盘
*
* @param key 缓存的key,通过该key来读写缓存,一般是URL
* @param data 缓存的数据
*/
protected void writeDiskCache(@NonNull String key, @NonNull byte[] data) {
if (TextUtils.isEmpty(key)) {
return;
}
String hashKey = StringUtil.keyToHashKey(key);
if (useMemory && mMemoryCache != null) {
mMemoryCache.put(hashKey, data);
}
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null) {
L.i(TAG, "缓存数据到磁盘[key:" + key + ",hashKey:" + hashKey + "]");
OutputStream out = null;
try {
DiskLruCache.Editor editor = mDiskLruCache.edit(hashKey);
out = editor.newOutputStream(DISK_CACHE_INDEX);
out.write(data, 0, data.length);
editor.commit();
out.flush();
out.close();
} catch (IOException e) {
FL.e(this, "writeDiskFailed[key:" + key + ",hashKey:" + hashKey + "]\n" + FL.getExceptionString(e));
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
/**
* 从磁盘读取缓存
*
* @param key 缓存的key,一般是原来的url
* @return 缓存数据
*/
protected byte[] readDiskCache(@NonNull String key) {
if (TextUtils.isEmpty(key)) {
return null;
}
String hashKey = StringUtil.keyToHashKey(key);
if (useMemory && mMemoryCache != null) {
final byte[] data = mMemoryCache.get(hashKey);
if (data != null && data.length != 0) {
return data;
}
}
synchronized (mDiskCacheLock) {
byte[] data = null;
L.i(TAG, "读取磁盘缓存数据[key:" + key + ",hashKey:" + hashKey + "]");
InputStream inputStream = null;
try {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(hashKey);
if (snapshot != null) {
inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
data = StreamUtil.readStream(inputStream);
return data;
}
} catch (IOException e) {
FL.e(this, "readDiskCacheFailed[key:" + key + ",hashKey:" + hashKey + "]\n" + FL.getExceptionString(e));
} catch (Exception e) {
FL.e(this, "readDiskCacheFailed[key:" + key + ",hashKey:" + hashKey + "]\n" + FL.getExceptionString(e));
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return null;
}
/**
* 删除一条缓存
*
* @param key 该缓存的key
*/
protected void removeCache(@NonNull String key) {
String hashKey = StringUtil.keyToHashKey(key);
if (mMemoryCache != null) {
mMemoryCache.remove(hashKey);
}
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null) {
try {
mDiskLruCache.remove(hashKey);
} catch (IOException e) {
FL.e(this, "removeCacheFailed[key:" + key + ",hashKey:" + hashKey + "]\n" + FL.getExceptionString(e));
}
}
}
}
/**
* 清除所有缓存
*/
protected void clearCache() {
if (mMemoryCache != null) {
mMemoryCache.evictAll();
}
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null) {
try {
mDiskLruCache.delete();
} catch (IOException e) {
FL.e(this, "clearCacheFailed" + FL.getExceptionString(e));
}
}
}
}
/**
* 关闭磁盘缓存,注意:
* 这个方法用于将DiskLruCache关闭掉,是和open()方法对应的一个方法。
* 关闭掉了之后就不能再调用DiskLruCache中任何操作缓存数据的方法,
* 通常只应该在Activity的onDestroy()方法中去调用close()方法。
*/
protected void closeDiskCache() {
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null) {
try {
mDiskLruCache.close();
} catch (IOException e) {
FL.e(this, "closeDiskCacheFailed" + FL.getExceptionString(e));
}
}
}
}
/**
* 同步内存中的缓存操作记录到日志文件(也就是journal文件)
* 注意:在写入缓存时需要flush同步一次,并不是每次写入缓存都要调用一次flush()方法的,频繁地调用并不会带来任何好处,
* 只会额外增加同步journal文件的时间。比较标准的做法就是在Activity的onPause()方法中去调用一次flush()方法就可以了
*/
protected void flushDiskCache() {
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null) {
try {
mDiskLruCache.flush();
} catch (IOException e) {
FL.e(this, "flushDiskCacheFailed" + FL.getExceptionString(e));
}
}
}
}
/**
* 获取缓存大小
*/
protected long getCacheSize() {
return mDiskLruCache.size();
}
/**
* 转换byte数组为String
*/
private static String bytesToHexString(byte[] bytes) {
// http://stackoverflow.com/questions/332079
StringBuilder sb = new StringBuilder();
for (byte aByte : bytes) {
String hex = Integer.toHexString(0xFF & aByte);
if (hex.length() == 1) {
sb.append('0');
}
sb.append(hex);
}
return sb.toString();
}
/**
* 生成缓存文件夹
*
* @param uniqueName 缓存文件夹名
* @return 缓存文件夹
*/
public static File getDiskCacheDir(Context context, String uniqueName) {
return new File(AndroidUtils.getDiskCacheDir(context) + File.separator + uniqueName);
}
}