版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Coder_Hcy/article/details/79979539
面试的时候被问过一次,如何对数据进行缓存,我答的数据库存储json字符串。被问到可不可以不用数据库,直接文件缓存对象。当然也是行的。之前看过郭神的博客:用lrucache与disklrucache缓存图片的。去年也仿着敲一个图片缓存工具类点击打开链接,今年来到新公司正好也遇到要离线加载数据的功能,换汤不换药的做了一个对象缓存到本地的工具类。
思路
方案
数据库我用的greendao,我发现greendao都有3.0的版本了,比2.0有较大的变化但更加方便了。
存对象(对象要序列化)
public void putObject(String httpurl,Object ob){
//将对象存入磁盘中
//将url转md5key
String key = hashKeyForDisk(httpurl);
try {
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot!=null){
//说明磁盘里有数据了,就删掉
mDiskLruCache.remove(key);
}
putObjectToMemoryCache(key,ob);
//开始往磁盘放数据
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
new Thread(new Put_Object_Runnable(editor,cacheDbBeanDao,ob,httpurl,key)).start();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG,"向磁盘中存入"+httpurl+"的对象是异常");
}
}
private class Put_Object_Runnable implements Runnable {
private DiskLruCache.Editor editor;
private CacheDbBeanDao cacheDbBeanDao;
private Object ob;
private String httpurl;
private String key;
public Put_Object_Runnable(DiskLruCache.Editor editor, CacheDbBeanDao cacheDbBeanDao, Object ob, String httpurl, String key) {
this.editor = editor;
this.cacheDbBeanDao = cacheDbBeanDao;
this.ob = ob;
this.httpurl = httpurl;
this.key = key;
}
@Override
public void run() {
if (editor != null) {
ObjectOutputStream oos =null;
try {
OutputStream outputStream = editor.newOutputStream(0);
oos= new ObjectOutputStream(outputStream);
oos.writeObject(ob);
oos.flush();
editor.commit();
//将记录存数据库
CacheDbBean cacheDbBean = cacheDbBeanDao.queryBuilder().where(CacheDbBeanDao.Properties.Key.eq(key)).unique();
if (cacheDbBean==null){
//第一次插入数据库
Log.i(TAG,"此数据第一次插入数据库"+httpurl);
cacheDbBeanDao.insert(new CacheDbBean(key,System.currentTimeMillis()));
}else{
Log.i(TAG,"此数据跟新数据库的时间"+httpurl);
cacheDbBean.setTime(System.currentTimeMillis());
cacheDbBeanDao.update(cacheDbBean);
}
} catch (IOException e) {
e.printStackTrace();
try {
editor.abort();
} catch (IOException e1) {
e1.printStackTrace();
}
}finally {
if (oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
flushCache();
}
}
从磁盘读取缓存数据
/**
* 根据http获取对象
* @param httpurl
* @return
*/
public Object getObjects(String httpurl){
//将url转md5key
String key = hashKeyForDisk(httpurl);
//先从内存中查找
Object objectFromMemoryCache = getObjectFromMemoryCache(key);
if (objectFromMemoryCache==null){
//内存中没有
Log.i(TAG,"内存中没有此"+httpurl+"的对象");
try {
//查找key 对应的硬盘缓存
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot != null) {
InputStream is = snapShot.getInputStream(0);
ObjectInputStream ois = new ObjectInputStream(is);
Log.i(TAG,"硬盘缓存中有此"+httpurl+"的对象");
return ois.readObject();
}else{
Log.i(TAG,"硬盘缓存中没有此"+httpurl+"的对象");
return null;
}
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return null;
}
}else{
Log.i(TAG,"内存中有此"+httpurl+"的对象");
return objectFromMemoryCache;
}
}
rxjava版
public Flowable<Object> getObjectHH(String httpurl){
return Flowable.just(httpurl).map(new Function<String, String>() {
@Override
public String apply(String url) throws Exception {
return hashKeyForDisk(url);
}
}).subscribeOn(Schedulers.io()).flatMap(new Function<String, Publisher<?>>() {
@Override
public Publisher<?> apply(String key) throws Exception {
Object objectFromMemoryCache = getObjectFromMemoryCache(key);
//先从内存中查找
if (objectFromMemoryCache==null){
//内存中没有
Log.i(TAG,"内存中没有此"+httpurl+"的对象");
//查找key 对应的硬盘缓存
try {
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot != null) {
InputStream is = snapShot.getInputStream(0);
ObjectInputStream ois = new ObjectInputStream(is);
Log.i(TAG,"硬盘缓存中有此"+httpurl+"的对象");
objectFromMemoryCache= ois.readObject();
return Flowable.just(objectFromMemoryCache);
}else{
Log.i(TAG,"硬盘缓存中没有此"+httpurl+"的对象");
return Flowable.just(objectFromMemoryCache);
}
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return Flowable.just(objectFromMemoryCache);
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return Flowable.just(objectFromMemoryCache);
}
}else{
Log.i(TAG,"内存中有此"+httpurl+"的对象");
return Flowable.just(objectFromMemoryCache);
}
}
});
判断缓存是否存在或者或者缓存是否有效
//判断一个接口是否有缓存以及缓存是否过期
protected boolean isHaveCacheOrCacheUseable(String httpurl){
//是否有缓存
String key=hashKeyForDisk(httpurl);
DiskLruCache.Snapshot snapShot = null;
try {
snapShot = mDiskLruCache.get(key);
} catch (IOException e) {
e.printStackTrace();
}
if (snapShot==null){
//说明磁盘没缓存
return false;
}else{
//有磁盘缓存就判断缓存是否有效
long currentTime = System.currentTimeMillis();//获取当前的系统时间
CacheDbBean cacheDbBean = cacheDbBeanDao.queryBuilder().where(CacheDbBeanDao.Properties.Key.eq(key)).unique();
if (cacheDbBean!=null){
if ((currentTime-cacheDbBean.getTime())>5*1000){
//超过5秒过期
return false;
}else{
return true;
}
}else{
return false;
}
}
}
效果:
副本:
package com.shan.library.utils;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.util.Log;
import android.util.LruCache;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import io.reactivex.Flowable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
* Created by hcy on 2018/4/17.
* func数据的三级缓存
*/
public class SELrucacheUtils {
private static final String TAG = "SELrucacheUtils";
private DiskLruCache mDiskLruCache;//硬盘缓存
private LruCache<String, Object> mMemoryCache;//内存缓存
private Context context;
private int maxMemory;//程序最大的可用内存
private DaoSession mDaoSession;
private static volatile SELrucacheUtils instance = null;
public static SELrucacheUtils getInstance(Context context, String cacheDirName){
if (instance == null) {
synchronized (SELrucacheUtils.class) {
if (instance == null) {
instance = new SELrucacheUtils(context,cacheDirName);
}
}
}
return instance;
}
//判断一个接口是否有缓存以及缓存是否过期
protected boolean isHaveCacheOrCacheUseable(String httpurl){
//是否有缓存
String key=hashKeyForDisk(httpurl);
DiskLruCache.Snapshot snapShot = null;
try {
snapShot = mDiskLruCache.get(key);
} catch (IOException e) {
e.printStackTrace();
}
if (snapShot==null){
//说明磁盘没缓存
return false;
}else{
//有磁盘缓存就判断缓存是否有效
long currentTime = System.currentTimeMillis();//获取当前的系统时间
CacheDbBean cacheDbBean = cacheDbBeanDao.queryBuilder().where(CacheDbBeanDao.Properties.Key.eq(key)).unique();
if (cacheDbBean!=null){
if ((currentTime-cacheDbBean.getTime())>5*1000){
//超过5秒过期
return false;
}else{
return true;
}
}else{
return false;
}
}
}
private DaoSession daoSession;
private CacheDbBeanDao cacheDbBeanDao;
public SELrucacheUtils(Context context, String cacheDirName) {
this.context = context;
//初始化数据库
//创建数据库shop.db"
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, "shop.db", null);
//获取可写数据库
SQLiteDatabase db = helper.getWritableDatabase();
//获取数据库对象
DaoMaster daoMaster = new DaoMaster(db);
//获取Dao对象管理者
daoSession = daoMaster.newSession();
cacheDbBeanDao=daoSession.getCacheDbBeanDao();
try {
File cacheDir = getDiskCacheDir(context, cacheDirName);//获取缓存路径
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
/**
* 缓存地址,应用版本,一个key对应几个文件,最大缓存值
*/
mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Object>(cacheSize) {
@Override
protected int sizeOf(String key, Object value) {
return super.sizeOf(key, value);
}
};
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 关闭硬盘缓存
*/
public void closeDiskLruCache() {
try {
if (mDiskLruCache != null) {
mDiskLruCache.close();//关闭缓存
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 计算所有图片的缓存大小
*/
public long countCache() {
if (mDiskLruCache != null) {
long size = mDiskLruCache.size();
return size;
} else {
return 0;
}
}
/**
* 删除缓存
*/
protected void deleteCache() throws IllegalStateException {
if (mDiskLruCache != null) {
try {
mDiskLruCache.delete();
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
throw new IllegalStateException("cache is closed");
}
}
}
/**
* 将对象保存到内存中
*
* @param key
* @param ob
*/
public void putObjectToMemoryCache(String key, Object ob) {
mMemoryCache.put(key, ob);
}
//同步缓存记录
private void flushCache() {
if (mDiskLruCache != null) {
try {
mDiskLruCache.flush();
} catch (IOException e) {
Log.i(TAG, "mDiskLruCache.flush() Error");
} catch (IllegalStateException e) {
Log.i(TAG, "mDiskLruCache.flush() ErrorE");
}
}
}
/**
* 将对象放入
* @param httpurl
* @param ob
*/
public void putObject(String httpurl,Object ob){
//将对象存入磁盘中
//将url转md5key
String key = hashKeyForDisk(httpurl);
try {
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot!=null){
//说明磁盘里有数据了,就删掉
mDiskLruCache.remove(key);
}
putObjectToMemoryCache(key,ob);
//开始往磁盘放数据
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
new Thread(new Put_Object_Runnable(editor,cacheDbBeanDao,ob,httpurl,key)).start();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG,"向磁盘中存入"+httpurl+"的对象是异常");
}
}
private class Put_Object_Runnable implements Runnable {
private DiskLruCache.Editor editor;
private CacheDbBeanDao cacheDbBeanDao;
private Object ob;
private String httpurl;
private String key;
public Put_Object_Runnable(DiskLruCache.Editor editor, CacheDbBeanDao cacheDbBeanDao, Object ob, String httpurl, String key) {
this.editor = editor;
this.cacheDbBeanDao = cacheDbBeanDao;
this.ob = ob;
this.httpurl = httpurl;
this.key = key;
}
@Override
public void run() {
if (editor != null) {
ObjectOutputStream oos =null;
try {
OutputStream outputStream = editor.newOutputStream(0);
oos= new ObjectOutputStream(outputStream);
oos.writeObject(ob);
oos.flush();
editor.commit();
//将记录存数据库
CacheDbBean cacheDbBean = cacheDbBeanDao.queryBuilder().where(CacheDbBeanDao.Properties.Key.eq(key)).unique();
if (cacheDbBean==null){
//第一次插入数据库
Log.i(TAG,"此数据第一次插入数据库"+httpurl);
cacheDbBeanDao.insert(new CacheDbBean(key,System.currentTimeMillis()));
}else{
Log.i(TAG,"此数据跟新数据库的时间"+httpurl);
cacheDbBean.setTime(System.currentTimeMillis());
cacheDbBeanDao.update(cacheDbBean);
}
} catch (IOException e) {
e.printStackTrace();
try {
editor.abort();
} catch (IOException e1) {
e1.printStackTrace();
}
}finally {
if (oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
flushCache();
}
}
/* *
* 存入对象
*//*
public void putObject(String httpurl,Object ob){
//将url转md5key
String key = hashKeyForDisk(httpurl);
Object objectFromMemoryCache = getObjectFromMemoryCache(key);
if (objectFromMemoryCache==null){
Log.i(TAG,"向内存中存入"+httpurl+"的对象");
putObjectToMemoryCache(key,ob);
}
//查找key 对应的硬盘缓存
try {
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot==null){
Log.i(TAG,"向磁盘中存入"+httpurl+"的对象");
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
}else{
Log.i(TAG,"向磁盘中已经存在"+httpurl+"的对象");
}
} catch (IOException e) {
Log.e(TAG,"向磁盘中存入"+httpurl+"的对象是异常");
e.printStackTrace();
}
}*/
/**
* 根据http获取对象
* @param httpurl
* @return
*/
public Object getObjects(String httpurl){
//将url转md5key
String key = hashKeyForDisk(httpurl);
//先从内存中查找
Object objectFromMemoryCache = getObjectFromMemoryCache(key);
if (objectFromMemoryCache==null){
//内存中没有
Log.i(TAG,"内存中没有此"+httpurl+"的对象");
try {
//查找key 对应的硬盘缓存
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot != null) {
InputStream is = snapShot.getInputStream(0);
ObjectInputStream ois = new ObjectInputStream(is);
Log.i(TAG,"硬盘缓存中有此"+httpurl+"的对象");
return ois.readObject();
}else{
Log.i(TAG,"硬盘缓存中没有此"+httpurl+"的对象");
return null;
}
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return null;
}
}else{
Log.i(TAG,"内存中有此"+httpurl+"的对象");
return objectFromMemoryCache;
}
}
public Flowable<Object> getObjectHH(String httpurl){
return Flowable.just(httpurl).map(new Function<String, String>() {
@Override
public String apply(String url) throws Exception {
return hashKeyForDisk(url);
}
}).subscribeOn(Schedulers.io()).flatMap(new Function<String, Publisher<?>>() {
@Override
public Publisher<?> apply(String key) throws Exception {
Object objectFromMemoryCache = getObjectFromMemoryCache(key);
//先从内存中查找
if (objectFromMemoryCache==null){
//内存中没有
Log.i(TAG,"内存中没有此"+httpurl+"的对象");
//查找key 对应的硬盘缓存
try {
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot != null) {
InputStream is = snapShot.getInputStream(0);
ObjectInputStream ois = new ObjectInputStream(is);
Log.i(TAG,"硬盘缓存中有此"+httpurl+"的对象");
objectFromMemoryCache= ois.readObject();
return Flowable.just(objectFromMemoryCache);
}else{
Log.i(TAG,"硬盘缓存中没有此"+httpurl+"的对象");
return Flowable.just(objectFromMemoryCache);
}
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return Flowable.just(objectFromMemoryCache);
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return Flowable.just(objectFromMemoryCache);
}
}else{
Log.i(TAG,"内存中有此"+httpurl+"的对象");
return Flowable.just(objectFromMemoryCache);
}
}
});
/* return Flowable.just(httpurl).map((url)->{
return hashKeyForDisk(url);
}).flatMap((key)->{
Object objectFromMemoryCache = getObjectFromMemoryCache(key);
//先从内存中查找
if (objectFromMemoryCache==null){
//内存中没有
Log.i(TAG,"内存中没有此"+httpurl+"的对象");
//查找key 对应的硬盘缓存
try {
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot != null) {
InputStream is = snapShot.getInputStream(0);
ObjectInputStream ois = new ObjectInputStream(is);
Log.i(TAG,"硬盘缓存中有此"+httpurl+"的对象");
objectFromMemoryCache= ois.readObject();
return Flowable.just(objectFromMemoryCache);
}else{
Log.i(TAG,"硬盘缓存中没有此"+httpurl+"的对象");
return Flowable.just(objectFromMemoryCache);
}
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return Flowable.just(objectFromMemoryCache);
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.i(TAG,"读取硬盘缓存异常");
return Flowable.just(objectFromMemoryCache);
}
}else{
Log.i(TAG,"内存中有此"+httpurl+"的对象");
return Flowable.just(objectFromMemoryCache);
}
}).subscribeOn(Schedulers.io());*/
}
/**
* 从内存中获取对象
* @param key
* @return
*/
public Object getObjectFromMemoryCache(String key) {
return mMemoryCache.get(key);
}
/**
* 使用MD5算法对传入的key进行加密并返回。
*/
private String hashKeyForDisk(String key) {
String cacheKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
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();
}
/**
* 获取缓存路径
*
* @param context
* @param cacheFileName
* @return
*/
private File getDiskCacheDir(Context context, String cacheFileName) {
String cachePath;
if (context != null) {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
if (context.getExternalCacheDir() != null) {
if (context.getExternalCacheDir().getPath() != null) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
} else {
cachePath = "/data/data/com.wyt.hcy.learningenglishapp/cache";
}
} else {
cachePath = context.getCacheDir().getPath();
}
} else {
cachePath = "/data/data/com.wyt.hcy.learningenglishapp/cache";
}
return new File(cachePath + File.separator + cacheFileName);
}
/**
* 获取当前应用程序的版本号。
*/
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;
}
}