1. 程式人生 > >Android 非同步載入圖片-LruCache和SD卡或手機快取-三級快取原理載入圖片

Android 非同步載入圖片-LruCache和SD卡或手機快取-三級快取原理載入圖片

非同步載入圖片的例子,網上也比較多,大部分用了HashMap<String, SoftReference<Drawable>> imageCache ,但是現在已經不再推薦使用這種方式了,因為從 Android 2.3 (API Level 9)開始,垃圾回收器會更傾向於回收持有軟引用或弱引用的物件,這讓軟引用和弱引用變得不再可靠。另外,Android 3.0 (API Level 11)中,圖片的資料會儲存在本地的記憶體當中,因而無法用一種可預見的方式將其釋放,這就有潛在的風險造成應用程式的記憶體溢位並崩潰,所以我這裡用得是LruCache來快取圖片,當儲存Image的大小大於LruCache設定的值,系統自動釋放記憶體,這個類是3.1版本中提供的,如果你是在更早的Android版本中開發,則需要匯入android-support-v4的jar包(這裡要注意咯)


為什麼寫這篇文章呢?

因為我之前做的專案中,也有非同步載入圖片,那時候用得是Thread去下載圖片,每次下載圖片都要new Thread去下載,而且還是併發去下載,每次都new 一個執行緒浪費記憶體,老闆說伺服器承受不起這麼多的連線,叫我改成先獲取一張圖片之後再去獲取下一張,這樣子儲存與伺服器的連線為一個,伺服器壓力小了,然後樓主就想到執行緒池,執行緒池很好的幫我們管理併發的問題,併發的問題解決了,可是後面又出問題了,圖片多了就出現OOM(OutOfMemory)異常,之後用了SoftReference,先用SoftReference中獲取圖片,SoftReference沒有就開執行緒去下載,老闆說你為什麼不把圖片在手機上做個快取呢,於是我用了手機快取,大概思路就是先從SoftReference中獲取圖片,如果SoftReference沒有就去手機快取中獲取,手機快取中沒有就開啟先從去下載,然後成功的解決了OOM的問題,前些天老闆要我重構下程式碼,我也覺得之前寫的程式碼耦合性太強,早就想改,然後之前看到guolin的

Android照片牆應用實現,再多的圖片也不怕崩潰的這篇文章,LruCache和滑動過程中取消下載任務,停下來的時候才去下載這2點比較好,值得我學習,然後我就將我的專案非同步載入這一塊改了下,發到這裡做個記錄吧,以後類似的非同步載入圖片直接拷貝程式碼,提交開發的效率

這篇文章做了哪些方面的優化

  1. 使用了執行緒池來管理下載任務
  2. 使用LruCache來快取圖片
  3. 使用手機來快取圖片
  4. GridView滑動的時候取消下載任務,靜止的時候進行下載,GridView滑動更加的流暢
  5. 降低了程式碼的耦合性,結構更加的清晰,便於以後重用

接下來我們先來看看專案的結構


  • FileUtils 檔案操作的工具類,提供儲存圖片,獲取圖片,判斷圖片是否存在,刪除圖片的一些方法,這個類比較簡單
  1. package com.example.asyncimageloader;  
  2. import java.io.File;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import android.content.Context;  
  6. import android.graphics.Bitmap;  
  7. import android.graphics.Bitmap.CompressFormat;  
  8. import android.graphics.BitmapFactory;  
  9. import android.os.Environment;  
  10. publicclass FileUtils {  
  11.     /** 
  12.      * sd卡的根目錄 
  13.      */
  14.     privatestatic String mSdRootPath = Environment.getExternalStorageDirectory().getPath();  
  15.     /** 
  16.      * 手機的快取根目錄 
  17.      */
  18.     privatestatic String mDataRootPath = null;  
  19.     /** 
  20.      * 儲存Image的目錄名 
  21.      */
  22.     privatefinalstatic String FOLDER_NAME = "/AndroidImage";  
  23.     public FileUtils(Context context){  
  24.         mDataRootPath = context.getCacheDir().getPath();  
  25.     }  
  26.     /** 
  27.      * 獲取儲存Image的目錄 
  28.      * @return 
  29.      */
  30.     private String getStorageDirectory(){  
  31.         return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ?  
  32.                 mSdRootPath + FOLDER_NAME : mDataRootPath + FOLDER_NAME;  
  33.     }  
  34.     /** 
  35.      * 儲存Image的方法,有sd卡儲存到sd卡,沒有就儲存到手機目錄 
  36.      * @param fileName  
  37.      * @param bitmap    
  38.      * @throws IOException 
  39.      */
  40.     publicvoid savaBitmap(String fileName, Bitmap bitmap) throws IOException{  
  41.         if(bitmap == null){  
  42.             return;  
  43.         }  
  44.         String path = getStorageDirectory();  
  45.         File folderFile = new File(path);  
  46.         if(!folderFile.exists()){  
  47.             folderFile.mkdir();  
  48.         }  
  49.         File file = new File(path + File.separator + fileName);  
  50.         file.createNewFile();  
  51.         FileOutputStream fos = new FileOutputStream(file);  
  52.         bitmap.compress(CompressFormat.JPEG, 100, fos);  
  53.         fos.flush();  
  54.         fos.close();  
  55.     }  
  56.     /** 
  57.      * 從手機或者sd卡獲取Bitmap 
  58.      * @param fileName 
  59.      * @return 
  60.      */
  61.     public Bitmap getBitmap(String fileName){  
  62.         return BitmapFactory.decodeFile(getStorageDirectory() + File.separator + fileName);  
  63.     }  
  64.     /** 
  65.      * 判斷檔案是否存在 
  66.      * @param fileName 
  67.      * @return 
  68.      */
  69.     publicboolean isFileExists(String fileName){  
  70.         returnnew File(getStorageDirectory() + File.separator + fileName).exists();  
  71.     }  
  72.     /** 
  73.      * 獲取檔案的大小 
  74.      * @param fileName 
  75.      * @return 
  76.      */
  77.     publiclong getFileSize(String fileName) {  
  78.         returnnew File(getStorageDirectory() + File.separator + fileName).length();  
  79.     }  
  80.     /** 
  81.      * 刪除SD卡或者手機的快取圖片和目錄 
  82.      */
  83.     publicvoid deleteFile() {  
  84.         File dirFile = new File(getStorageDirectory());  
  85.         if(! dirFile.exists()){  
  86.             return;  
  87.         }  
  88.         if (dirFile.isDirectory()) {  
  89.             String[] children = dirFile.list();  
  90.             for (int i = 0; i < children.length; i++) {  
  91.                 new File(dirFile, children[i]).delete();  
  92.             }  
  93.         }  
  94.         dirFile.delete();  
  95.     }  
  96. }<span style="font-family:Times New Roman;font-size:14px;">  
  97. </span>  
  1. package com.example.asyncimageloader;  
  2. publicclass Images {  
  3.     publicfinalstatic String[] imageThumbUrls = new String[] {  
  4.             "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s160-c/A%252520Photographer.jpg",  
  5.             "https://lh4.googleusercontent.com/--dq8niRp7W4/URquVgmXvgI/AAAAAAAAAbs/-gnuLQfNnBA/s160-c/A%252520Song%252520of%252520Ice%252520and%252520Fire.jpg",  
  6.             "https://lh5.googleusercontent.com/-7qZeDtRKFKc/URquWZT1gOI/AAAAAAAAAbs/hqWgteyNXsg/s160-c/Another%252520Rockaway%252520Sunset.jpg",  
  7.             "https://lh3.googleusercontent.com/--L0Km39l5J8/URquXHGcdNI/AAAAAAAAAbs/3ZrSJNrSomQ/s160-c/Antelope%252520Butte.jpg",  
  8.