package com.example.ipcplayer.cache;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.example.ipcplayer.cache.db.CacheDBHelper;
import com.example.ipcplayer.utils.LogUtil;
import com.example.ipcplayer.utils.StringUtil;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.LruCache;
public class DataCache{
private final String TAG = DataCache.class.getSimpleName();
private static DataCache mInstance;
private Context mContext;
/** 缓存体*/
private LruCache<String, CacheEntity> mCache;
/** 缓存体的最大空间 (1M)*/
private long mMaxSpace = 1*1024*1024;
/** 已使用的缓存空间*/
private long mCachedSpace = 0;
/** 击中次数*/
private int mHitCount = 0;
/** 丢失次数*/
private int mMissCount = 0;
/** 待缓存的数据集合 */
private ConcurrentHashMap<Integer, CacheEntity> mUpdateSet = new ConcurrentHashMap<Integer, CacheEntity>();
/** 缓存的最大阀值 达到此值后就存入数据库 */
private final int UPDATE_THRESHOLD = 10;
@SuppressLint("NewApi")
private DataCache(Context context){
mContext = context;
mCache = new LruCache<String, CacheEntity>(2){
//缓存数据的处理
// @Override
// public void recycleEldestEntry(CacheEntity eldestRef) {
// mCachedSpace -= eldestRef.caculateMemSize();
// validate();
// }
};
}
public static DataCache getInstance(Context context){
if (mInstance != null)
return mInstance;
synchronized (DataCache.class) {
if (mInstance == null)
mInstance = new DataCache(context);
}
return mInstance;
}
/**
* 设置内存缓存的最大存储空间
* @param maxSpace
*/
public void setMaxSpace(long maxSpace){
mMaxSpace = maxSpace;
}
/**
* 设置数据库的最多缓存条数
* @param count
*/
public void setDBMaxRowCount(int count){
CacheDBHelper.getInstance(mContext).setMaxCount(count);
}
/**
* 从缓存模块中读取缓存的数据
* @param key
* @param entity
* @return
* @throws CacheExpiredException
* @throws CacheUncachedException
*/
@SuppressWarnings("unused")
@SuppressLint("NewApi")
public Cacheable get(String key,CacheEntity entity)
throws CacheExpiredException, CacheUncachedException {
if(StringUtil.isEmpty(key) || entity == null){
return null;
}
CacheEntity cacheEntity = mCache.get(key);
LogUtil.d(TAG, "readFromMemCache entry : " + cacheEntity);
if(cacheEntity == null){
cacheEntity = readFromDatabase(key,entity.getCacheable());
LogUtil.d(TAG, "readFromDB entry : " + cacheEntity.toString());
}
Cacheable cacheObject = null;
if(cacheEntity != null){
cacheEntity.setLastUsedTime(System.currentTimeMillis());
mUpdateSet.put(cacheEntity.hashCode(), cacheEntity);
if(mUpdateSet.size() > UPDATE_THRESHOLD){
updateDB();
}
cacheObject = cacheEntity.getCacheable();
}
if(cacheObject == null){
mMissCount ++ ;
LogUtil.d(TAG + " datacache miss, mMissCount = " + mMissCount);
}else{
mHitCount ++;
LogUtil.d(TAG + " datacache hit, mHitCount = " + mHitCount);
}
cacheEntity.setCacheable(cacheObject);//有用吗?
if(cacheObject == null){
throw new CacheUncachedException();
}
if(cacheEntity.isExpired()){
throw new CacheExpiredException();
}
return cacheObject;
}
/**
* 从数据库读取缓存数据
* @param key
* @return CacheEntity
*/
private CacheEntity readFromDatabase(String key,Cacheable obj){
LogUtil.d(TAG + " read from db key = " + key);
CacheEntity entity = new CacheEntity();
entity.setCacheable(obj);
entity = CacheDBHelper.getInstance(mContext).get(key,entity);
putIntoMemCache(key, entity);
return entity;
}
/**
* 数据放入缓存模块
* @param key
* @param obj
* @param valideTime
* @return
*/
public Cacheable put(String key, Cacheable obj, long valideTime){
if(StringUtil.isEmpty(key) || obj == null || !obj.isCacheable() || valideTime < 0){
return null;
}
long cacheTime = System.currentTimeMillis();
CacheEntity entity = new CacheEntity(key, obj, cacheTime, valideTime);
putIntoMemCache(key,entity);
validate();
saveToDB(entity);
return obj;
}
/**
* 更新数据库中缓存数据
*/
private void updateDB() {
LogUtil.d(TAG + " updateDB ");
Set<Integer> keys = mUpdateSet.keySet();
CacheEntity entity = null;
for (Integer key : keys) {
entity = mUpdateSet.remove(key);
CacheDBHelper.getInstance(mContext).update(entity);
}
}
/**
* 缓存数据放入内存缓存
* @param key
* @param entity
*/
@SuppressLint("NewApi")
private void putIntoMemCache(String key, CacheEntity entity){
LogUtil.d(TAG + " putintoMem ");
if(StringUtil.isEmpty(key) || entity == null){
return ;
}
mCache.put(key, entity);
mCachedSpace += entity.calculateMemSize();// 计算缓存所占空间
LogUtil.d(TAG + " putintoMen, mCachedSpace = " + mCachedSpace);
}
/**
* 检查缓存空间
*/
private void validate(){
if(mCachedSpace > mMaxSpace){
LogUtil.d(TAG + " cache space is expired ");
}
}
/**
* 缓存数据保存到数据库
* @param entity
*/
private void saveToDB(CacheEntity entity){
if(entity == null){
return ;
}
LogUtil.d(TAG + " saveToDB , entity = " + entity.toString());
CacheDBHelper.getInstance(mContext).insert(entity);
}
/**
* 获取命中率
*
* @return 命中率(%)
*/
public int getHitRate() {
int accesses = mHitCount + mMissCount;
int hitPercent = accesses != 0 ? (100 * mHitCount / accesses) : 0;
return hitPercent;
}
/**
* 清空内存缓存
*/
@SuppressLint("NewApi")
public void clearMemSpace(){
updateDB();
if(mCache != null){
mCache.evictAll();
}
mCachedSpace = 0;
}
}