1.DiskLruCache的建立
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)使用此方法建立DiskLruCache,此方法共接收四個參數
第一個參數表示磁盤緩存在檔案系統中的路徑,具體為/sdcard/Android/data/package_name/cache目錄,其中package_name為應用程式的包名,當應用程式被解除安裝後此目錄會一并删除。
擷取磁盤緩存在檔案系統中的路徑具體代碼:
public File getDiskCacheDir(Context context,String uniqueName){
String cachePath;
//判斷sd卡是否有讀寫權限,是否可被移除;當具有讀寫權限或者不可被移除,使用前者;否則使用後者
if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageSt
ate())||!Environment.isExternalStorageRemovable(){
//擷取緩存路徑;
cachePath = context.getExternalCacheDir().getPath();
}else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath+File.separator+uniqueName);
}
前者擷取的路徑為:/sdcard/Android/data/package_name/cache後者擷取的路勁為: /data/data/package_name/cache,uniqueName為某一類型的資料的值,如圖檔類型可設定為bitmap,則最終擷取的緩存路徑為:/sdcard/Android/data/package_name/cache/bitmap
第二個參數為應用的版本号一般設定為1即可,當版本發生改變時會清空之前的緩存檔案。
第三個參數為單個節點所對應的資料個數,設定為1即可,第四個參數表示緩存容器的總大小,如50MB,當需要緩存的檔案超過該值時會自動清除部分緩存,進而保證緩存檔案的大小不超過該緩存容器的大小。
建立一個open()可寫為:
File cacheDir = getDiskCacheDir(context,"bitmap");
if(!cacheDir.exists()){
//如果不存在,建立此檔案夾
cacheDir.mkdirs();
}
try{
mDiskLruCache = DiskLruCache.open(cacheDir,,,*1024*1024);
} catch (IOException e) {
e.printStackTrace();
}
擷取DiskLruCache執行個體後,接着就可以寫入緩存
首先下載下傳一張圖檔如http://img.my.csdn.NET/uploads/201309/01/1378037235_7476.jpg
private boolean downLoadUrlToStream(String url, final OutputStream outputStream){
OkHttpClient mOkHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
Response response = mOkHttpClient.newCall(request).execute();
InputStream inputStream = response.body().byteStream();
in = new BufferedInputStream(inputStream, * );
out= new BufferedOutputStream(outputStream, * );
int b;
while ((b = in.read()) != -) {
out.write(b);
}
return true;
}catch (IOException e) {
e.printStackTrace();
}finally {
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
DiskLruCache緩存的添加是通過Editor完成,然後将圖檔的url轉化為key,根據key然後調用edit(String key),可以擷取Editor對象,然後調用Editor對象的newOutputStream(int index)方法即可擷取OutputStream對象,得到OutputStream對象和url後,使用上述downLoadUrlToStream(String url, final OutputStream outputStream),就可以将圖檔進行寫入,但此時并沒有寫入檔案系統還需調用Editor的commit()方法。
由于url中可能包含特殊字元,使其不能直接作為key,是以可使用MD5編碼使其轉化為key;
public static String hashKeyForUrl(String url){
String catchKey;
try {
final MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(url.getBytes());
catchKey = bytesToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
catchKey = String.valueOf(url.hashCode());
}
return catchKey;
}
private static String bytesToHexString(byte[] bytes){
StringBuilder sb = new StringBuilder();
for (int i =;i<bytes.length;i++){
String hex = Integer.toHexString(&bytes[i]);
if(hex.length()==){
sb.append();
}
sb.append(hex);
}
return sb.toString();
}
然後就可以擷取Editor對像,并進行寫入磁盤操作
public void ImageLoading(){
new Thread(new Runnable() {
@Override
public void run() {
String url = "https://img-my.csdn.net/uploads/201309/01/1378
037235_7476.jpg";
String key = hashKeyForUrl(url);
try {
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if(editor!=null){
OutputStream outputStream = editor.newOutputStream(
);
if(downLoadUrlToStream(url,outputStream)){
//進行寫入操作
editor.commit();
}else {
//下載下傳過程中出現異常退出操作
editor.abort();
}
}
//這個方法用于将記憶體中的操作記錄同步到日志檔案(也就是journal文
件)當中。
mDiskLruCache.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
讀取緩存
緩存讀取也需要将url轉化為key,按照MD5上述編碼方式轉化即可。然後通過DiskLruCache的get(String key)方法擷取Snapshot對象,接着通過Snapshot對象的getInputStream(int index)即可擷取InputStream,然後就可以擷取Bitmap對象。為了避免OOM需要對圖檔壓縮後,再将其顯示在ImageView上面。
Bitmap bitmap = null;
String url = "https://img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
String key = hashKeyForUrl(url);
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null) {
FileInputStream fileInputStream = (FileInputStream) snapshot.getInputSt
ream();
//擷取檔案流對應的檔案描述符
FileDescriptor fileDescriptor = fileInputStream.getFD();
//把圖檔壓縮成100*100像素
bitmap = decodeBitmapFromFileDescriptor(fileDescriptor, ,);
}
圖檔壓縮
public static Bitmap decodeBitmapFromFileDescriptor(FileDescriptor fileDescriptor,int reqWidth,int reqHeight){
final BitmapFactory.Options options = new BitmapFactory.Options();
//inJustDecodeBounds 設定為true時BitmapFactory隻會解析原始圖檔寬高,不會加載
圖檔
options.inJustDecodeBounds = true;
BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeigh
t);
//當希望擷取Bitmap執行個體時需要設定inJustDecodeBounds為false;
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFileDescriptor(fileDescriptor,null,options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
//擷取原始圖檔的寬高
final int realWidth = options.outWidth;
final int realHeight = options.outHeight;
//定義采樣率;
int inSampleSize = ;
if(realHeight>reqHeight||realWidth>reqWidth){
final int halfHeight = realHeight/;
final int halfWidth = realWidth/;
while ((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize
)>=reqWidth){
inSampleSize*=;
}
}
return inSampleSize;
}
壓縮圖檔時由于是FileInputStream方式,FileInputStream是一種有序的檔案流,兩次調用decodeStream影響了檔案流的位置屬性,導緻第二次調用時傳回null,為了解決此問題可以使用檔案流對應的檔案描述符,然後再進行加載。
Android DiskLruCache完全解析,硬碟緩存的最佳方案