DiskLruCache与LruCache区别:
LruCache是将数据缓存到内存中去;
DiskLruCache是将数据缓存到外部存储;例如:可以将网络下载的图片永久的缓存到手机外部存储中去,并可以将缓存数据取出来使用。
DiskLruCache地址:https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java
或者可以在Jake大神的Github上找到:https://github.com/JakeWharton/DiskLruCache
DiskLruCache常用方法:
DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
打开一个缓存目录,如果没有则首先创建。directory:指定数据缓存地址 appVersion:APP版本号,当版本号改变时,缓存数据会被清除 valueCount:同一个key可以对应多少文件 maxSize:最大可以缓存的数据量。
通过key可以获得一个DiskLruCache.Editor,通过Editor可以得到一个输出流,进而缓存到本地存储上。
强制缓存文件保存到文件系统。
通过key值来获得一个Snapshot,如果Snapshot存在,则移动到LRU队列的头部来,通过Snapshot可以得到一个输入流InputStream。
缓存数据的大小,单位是byte。
根据key值来删除对应的数据,如果该数据正在被编辑,则不能删除。
关闭缓存并且删除目录下所有的缓存数据,即使有的数据不是由DiskLruCache缓存到本目录的。
关闭DiskLruCache,缓存数据会保留在外存中。
判断DiskLruCache是否关闭,返回true表示已关闭。
File getDirectory() 缓存数据的目录。
如何使用DiskLruCache:
1. 因为要操作外部存储,所以必须要先加上权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
另外要从网络下载图片,还要加上权限:
<uses-permission android:name="android.permission.INTERNET" />
2. DiskLruCache是在外部存储上(如SD卡),所以首先判断外部存储是否存在:
/**
* Get a usable cache directory (external if available, internal otherwise).
* external:如:/storage/emulated/0/Android/data/package_name/cache
* internal 如:/data/data/package_name/cache
*
* @param
* @param
* @return
*/
public static File getDiskCacheDir(Context context, String uniqueName){
final
? context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
return
new
}
(1)、首先判断外部缓存是否被移除或已存满,如果已存满或者外存储被移除,则缓存目录=context.getCacheDir().getPath(),即存到/data/data/package_name/cache
这个文件目录下;
(2)、反之缓存目录=context.getExternalCacheDir().getPath(),即存到/storage/emulated/0/Android/data/package_name/cache这个外部存储目录中。
外部存储可以分为两种:
一种如上面这种路径( /storage/emulated/0/Android/data/package_name/cache),当应用卸载后,存储数据也会被删除;
另一种是永久存储,即使应用被卸载,存储的数据依然存在,存储路径如:/storage/emulated/0/mDiskCache,可以通过Environment.getExternalStorageDirectory()
.getAbsolutePath()+"/mDiskCache"来获得路径。
3. 根据URL下载一个在线图片,并把它写到输出流OutputStream中:
/**
* Download a bitmap from a URL and write the content to an output stream.
*
* @param
* @return
*/
private boolean downloadUrlToStream(String urlString, OutputStream outputStream){
HttpURLConnection urlConnection =
null;
BufferedOutputStream out =
null;
BufferedInputStream in =
null;
try
final URL url =
new
urlConnection = (HttpURLConnection) url.openConnection();
in =
new
out =
new
int
while ((b = in.read()) != -
1) {
out.write(b);
}
return
true;
}
catch
Log.e(TAG,
"Error in downloadBitmap - "
}
finally
if (urlConnection !=
null) {
urlConnection.disconnect();
}
try
if (out !=
null) {
out.close();
}
if (in !=
null) {
in.close();
}
}
catch (
final
}
}
return
false;
}
private
static
final
int MAX_SIZE =
10 *
1024 *
1024;
//10MB
private
private void initDiskLruCache(){
if (diskLruCache ==
null
try
File cacheDir = CacheUtil.getDiskCacheDir(
this,
"CacheDir");
if
cacheDir.mkdirs();
}
//初始化DiskLruCache
diskLruCache = DiskLruCache.open(cacheDir,
1,
1, MAX_SIZE);
}
catch
e.printStackTrace();
}
}
}
下载图片时需要放在异步线程里,这里放在了AsyncTask的doInBackground中:
@Override
protected Boolean doInBackground(Object... params){
try
String key = Util.hashKeyForDisk(Util.IMG_URL);
DiskLruCache diskLruCache = (DiskLruCache) params[
0];
//得到DiskLruCache.Editor
DiskLruCache.Editor editor = diskLruCache.edit(key);
if (editor !=
null) {
OutputStream outputStream = editor.newOutputStream(
0);
if
publishProgress(
"");
//写入缓存
editor.commit();
}
else
//写入失败
editor.abort();
}
}
diskLruCache.flush();
}
catch
e.printStackTrace();
return
false;
}
return
true;
}
上面代码中有个hashKeyForDisk()方法,其作用是把图片URL经过MD5加密生成唯一的key值,避免了URL中可能含有非法字符问题,hashKeyForDisk()代码如下:
/**
* A hashing method that changes a string (like a URL) into a hash suitable for using as a
* disk filename.
*/
public static String hashKeyForDisk(String key){
String cacheKey;
try
final MessageDigest mDigest = MessageDigest.getInstance(
"MD5");
mDigest.update(key.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
}
catch
cacheKey = String.valueOf(key.hashCode());
}
return
}
private static String bytesToHexString(byte[] bytes){
// http://stackoverflow.com/questions/332079
StringBuilder sb =
new
for (
int i =
0; i < bytes.length; i++) {
String hex = Integer.toHexString(
0xFF
if (hex.length() ==
1) {
sb.append(
'0');
}
sb.append(hex);
}
return
}
经过上面的代码,我们已经可以看到图片已经缓存到 /storage/emulated/0/Android/data/package_name/cache/CacheDir
这个目录下了:
cacheDir.png
第一个标识为110.78kb大小的就是我们缓存下来的图片,它的名字正是由图片的URL经过MD5加密得到的,
它下面的journal文件是用来记录的,来看里面的内容:
journal.png
第一行:libcore.io.DiskLruCache固定写死
第二行:DiskLruCache版本号
第三行:APP版本号,由open()方法的参数appVersion传入
第四行:同一个key可以对应多少文件,由open()方法的参数valueCount传入,一般为1
第五行:空格
第六行:以DIRTY开头,后面跟着的是图片的key值,表示准备缓存这张图片,当调用DiskLruCache的edit()时就会生成这行记录
第七行: 以CLEAN开头,后面跟着的是图片的Key值和大小,当调用editor.commit()时会生成这条记录,表示缓存成功;如果调用editor.abort()表示缓存失败,则会生成REMOVE开头的表示删除这条数据。
5、通过diskLruCache.get(key)得到DiskLruCache.Snapshot,key是经过MD5加密后那个唯一的key,接着使用Snapshot.getInputStream()可以得到输入流InputStream ,进而得到缓存图片:
private Bitmap getCache() {
try {
String key = Util.hashKeyForDisk(Util.IMG_URL);
DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
if (snapshot != null) {
InputStream in = snapshot.getInputStream(0);
return BitmapFactory.decodeStream(in);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private Bitmap getCache() {
try {
String key = Util.hashKeyForDisk(Util.IMG_URL);
DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
if (snapshot != null) {
InputStream in = snapshot.getInputStream(0);
return BitmapFactory.decodeStream(in);
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Bitmap bitmap = getCache();
if (bitmap != null) {
iv_img.setImageBitmap(bitmap);
}
Bitmap bitmap = getCache();
if (bitmap != null) {
iv_img.setImageBitmap(bitmap);
}
效果图:
cache.png