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

这个目录下了:



androidstudio 手机调试磁盘越来越大_外部存储



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