天天看點

DiskLruCache磁盤緩存

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完全解析,硬碟緩存的最佳方案