DiskLruCache 不同於LruCache,LruCache是將資料快取到記憶體中去,而DiskLruCache是外部快取,例如可以將網路下載的圖片永久的快取到手機外部儲存中去,並可以將快取資料取出來使用,DiskLruCache不是google官方所寫,但是得到了官方推薦,DiskLruCache沒有編寫到SDK中去,如需使用可直接copy這個類到專案中去。
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:**最大可以快取的資料量 |
Editor edit(String key) | 通過key可以獲得一個DiskLruCache.Editor,通過Editor可以得到一個輸出流,進而快取到本地儲存上 |
void flush() | 強制緩衝檔案儲存到檔案系統 |
Snapshot get(String key) | 通過key值來獲得一個Snapshot,如果Snapshot存在,則移動到LRU佇列的頭部來,通過Snapshot可以得到一個輸入流InputStream |
long size() | 快取資料的大小,單位是byte |
boolean remove(String key) | 根據key值來刪除對應的資料,如果該資料正在被編輯,則不能刪除 |
void delete() | 關閉快取並且刪除目錄下所有的快取資料,即使有的資料不是由DiskLruCache 快取到本目錄的 |
void close() | 關閉DiskLruCache,快取資料會保留在外存中 |
boolean isClosed() | 判斷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 context The context to use
* @param uniqueName A unique directory name to append to the cache dir
* @return The cache dir
*/
public static File getDiskCacheDir(Context context, String uniqueName) {
final String cachePath = Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||!isExternalStorageRemovable()
? context.getExternalCacheDir().getPath()
: context.getCacheDir().getPath();
return new File(cachePath + File.separator + uniqueName);
}
複製程式碼
(1)、首先判斷外部快取是否被移除或已存滿,如果已存滿或者外儲存被移除,則快取目錄=context.getCacheDir().getPath(),即存到 /data/data/package_name/cache 這個檔案系統目錄下; (2)、反之快取目錄=context.getExternalCacheDir().getPath(),即存到 /storage/emulated/0/Android/data/package_name/cache 這個外部儲存目錄中,PS:外部儲存可以分為兩種:一種如上面這種路徑 (/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 urlString The URL to fetch
* @return true if successful, false otherwise
*/
private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
HttpURLConnection urlConnection = null;
BufferedOutputStream out = null;
BufferedInputStream in = null;
try {
final URL url = new URL(urlString);
urlConnection = (HttpURLConnection) url.openConnection();
in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE);
out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);
int b;
while ((b = in.read()) != -1) {
out.write(b);
}
return true;
} catch (Exception e) {
Log.e(TAG, "Error in downloadBitmap - " + e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (final IOException e) {
}
}
return false;
}
複製程式碼
4、上面已經下載了圖片,接著初始化DiskLruCache,並使用DiskLruCache.Editor準備快取:
private static final int MAX_SIZE = 10 * 1024 * 1024;//10MB
private DiskLruCache diskLruCache;
private void initDiskLruCache() {
if (diskLruCache == null || diskLruCache.isClosed()) {
try {
File cacheDir = CacheUtil.getDiskCacheDir(this, "CacheDir");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
//初始化DiskLruCache
diskLruCache = DiskLruCache.open(cacheDir, 1, 1, MAX_SIZE);
} catch (IOException e) {
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 (downloadUrlToStream(Util.IMG_URL, outputStream)) {
publishProgress("");
//寫入快取
editor.commit();
} else {
//寫入失敗
editor.abort();
}
}
diskLruCache.flush();
} catch (IOException e) {
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 (NoSuchAlgorithmException e) {
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
}
private static String bytesToHexString(byte[] bytes) {
// http://stackoverflow.com/questions/332079
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();
}
複製程式碼
經過上面的程式碼,我們已經可以看到圖片已經快取到 /storage/emulated/0/Android/data/package_name/cache/CacheDir 這個目錄下了:
第一個標識為110.78kb大小的就是我們快取下來的圖片,它的名字正是由圖片的URL經過MD5加密得到的,它下面的journal檔案是用來記錄的,來看裡面的內容:
第一行: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;
}
複製程式碼
Bitmap bitmap = getCache();
if (bitmap != null) {
iv_img.setImageBitmap(bitmap);
}
複製程式碼
效果圖:
完整Demo地址: http://download.csdn.net/detail/u013700502/9883037