Android基於DiskLruCache做一個數據物件的快取工具
阿新 • • 發佈:2019-01-30
面試的時候被問過一次,如何對資料進行快取,我答的資料庫儲存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;
}
}