在最初的Android學習中,對于從網絡上去下載下傳圖檔是一個比較繁瑣的操作,需要自己去開啟子線程執行下載下傳。假如是一兩張圖檔還好,如果是成百上千張圖檔同時去加載,這将會給系統帶來很大的資源開銷,好在總有“大神”存在,現在已經有很多開源架構可以去解決網絡加載圖檔的問題。
Volley架構是一個使用率較高的第三方開源架構,在Volley中有ImageRequest類可以實作圖檔加載,但是ImageRequest并未進行圖檔的緩存,如果是重複的去加載圖檔這将會給使用者帶來額外的流量開銷。Volley中還有一個ImageLoader内部也是使用ImageRequest來實作的,不過ImageLoader明顯要比ImageRequest更加高效,因為它不僅可以幫我們對圖檔進行緩存,還可以過濾掉重複的連結,避免重複發送請求。ImageLoader的用法主要分為如下四步:
1. 建立一個RequestQueue對象
2. 建立ImageLoader對象
3. 建立ImageListener對象
4. 調用ImageLoader中的get()方法加載圖檔
一、建立RequestQueue
建立一個自定義的MyApplication類(名字自定義)繼承Application類,重寫onCreate()方法,建立RequestQueue。
public class MyApplication extends Application {
private static RequestQueue requestQueue;
@Override
public void onCreate() {
super.onCreate();
requestQueue = Volley.
newRequestQueue(getApplicationContext());
}
public static RequestQueue getQueue(){
return requestQueue;
}
}
這種建立的RequestQueue是一個全局的變量,還得再清單檔案的application标簽中添加name屬性,如下所示:
<application
android:name="com.example.imageloader.MyApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
二、建立ImageLoader
ImageLoader的構造方法如下:
ImageLoader(RequestQueue queue, ImageCache imageCache)
第一參數是一個RequestQueue對象,在第一部已經建立好了,第二個參數是ImageCache緩存對象,實作圖檔的緩存,但是ImageCache是一個接口,這裡需要去建立一個類實作該接口,在該接口中有兩個方法getBitmap()和putBitmap需要實作,分别是擷取緩存中的圖檔和将圖檔存入緩存。
使用ImageCache時還需要使用到Android提供的緩存類工具LruCache,将圖檔以鍵值對的方式存儲在LruCache中,在BitmapCache的構造方法中建立LruCache對象并重寫sizeOf()方法。
public class BitmapCache implements ImageCache {
// LruCatch是Android提供的緩存工具
private LruCache<String, Bitmap> mCache;
public BitmapCache(){
// 擷取系統給應用程式配置設定的記憶體
int maxMemory = (int) Runtime.getRuntime().maxMemory();
// 取1/8作為圖檔緩存
int maxCatch = maxMemory / ;
// 建立緩存
mCache = new LruCache<String, Bitmap>(maxCatch){
//測量Bitmap的大小 ,便于統計緩存使用總量
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getWidth() * value.getHeight();
}
};
}
/**
* 擷取緩存的圖檔
* url:下載下傳圖檔時傳入的圖檔網址
*/
@Override
public Bitmap getBitmap(String url) {
}
/**
* 緩存圖檔,以url作為key值
*/
@Override
public void putBitmap(String url, Bitmap bitmap) {
}
sizeOf()方法是用來測量下載下傳的圖檔的大小,每當下載下傳一個圖檔後就會在圖檔使用緩存的總數上加上該圖檔的大小,以便監控緩存的使用情況,當緩存使用超過設定值後将會自動對緩存進行清理。
下面去實作圖檔在記憶體中的存儲和讀取操作,如下所示:
/**
* 擷取緩存的圖檔
* url:下載下傳圖檔時傳入的圖檔網址
*/
@Override
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
/**
* 緩存圖檔,以url作為key值
*/
@Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
接下來就可以建立ImageLoader對象,我們也可以在MyApplication中建立ImageCache對象,把ImageCache設定成全局的。
public class MyApplication extends Application {
private static RequestQueue requestQueue;
public static BitmapCache bitmapCatch = new BitmapCache();
@Override
public void onCreate() {
super.onCreate();
requestQueue = Volley.newRequestQueue(getApplicationContext());
}
public static RequestQueue getQueue(){
return requestQueue;
}
}
建立ImageLoader對象。
三、建立ImageListener
ImageListener是用來對圖檔加載的一個監聽類,通過ImageListener可以設定圖檔加載中、加載失敗的圖檔。
ImageListener listener = ImageLoader.getImageListener(iv1, R.drawable.default,
R.drawable.failed);
第一個參數傳入顯示圖檔的ImageView空間對象,第二個參數出入加載過程中現實的圖檔id,第三個參數傳入顯示加載失敗圖檔的id。
四、調用get()方法
最後隻需要利用ImageLoader對象調用get(String requestUrl, final ImageListener listener)方法,分别傳入圖檔的網址和ImageListener對象即可。
下面我們來看下效果,具體的布局因人而異,這裡就不做介紹。
雖然按照上面的步驟達到了最終的效果,圖檔加載成功了,但是其中圖檔加載與緩存的一個過程是怎樣的呢?下面就簡單的把它的邏輯過程做一個羅列。
1. UI請求資料,調用BitmapCache中get()方法,使用唯一的Key值索引Memory Cache中的Bitmap,即參入的String參數。
2. 緩存搜尋,如果能找到Key值對應的Bitmap,則傳回資料。否則執行第三步。
3. 下載下傳圖檔:啟動異步線程,從資料源下載下傳資料(Web)。
4. 若下載下傳成功,将資料同時寫入緩存,并将Bitmap顯示在UI中。
但是上面并沒有将資料緩存到本地,如要再加一級本地緩存隻需要再去定義兩個方法,一個寫入SD,另一個讀SD卡,需要注意的是讀取緩存時記憶體緩存優先于硬碟緩存。
1. UI請求資料,調用BitmapCache中get()方法,使用唯一的Key值索引Memory Cache中的Bitmap,即參入的String參數;
2. 緩存搜尋,如果能找到Key值對應的Bitmap,則傳回資料,否則執行第三步;
3. 使用唯一Key值對應的檔案名,檢索SDCard上的檔案;
4. 如果有對應檔案,使用BitmapFactory.decode*方法,解碼Bitmap并傳回資料,同時将資料寫入緩存。如果沒有對應檔案,執行第五步
5. 啟動異步線程下載下傳圖檔,從資料源下載下傳資料(Web);
6. 若下載下傳成功,将資料同時寫入緩存,并将Bitmap顯示在UI中。
代碼如下:
/**
* 擷取緩存的圖檔
* url:下載下傳圖檔時傳入的圖檔網址
*/
@Override
public Bitmap getBitmap(String url) {
Bitmap bitmap = mCache.get(url);
Log.d("Tag","擷取緩存 = "+bitmap);
// 緩存沒有就從SD卡擷取
if(bitmap == null){
// 擷取圖檔的名字
String imageName = url.substring(url.lastIndexOf("/") + );
// 傳回值不為空說明SD有對應圖檔,并存入緩存
bitmap = getBitmapFromSD(imageName);
if(bitmap != null){
mCache.put(url, bitmap);
}
}
return bitmap;
}
/**
* 緩存圖檔,以url作為key值
*/
@Override
public void putBitmap(String url, Bitmap bitmap) {
Log.d("Tag", "存入緩存");
mCache.put(url, bitmap);
// 網絡下載下傳後存入SD
saveBitmapToSD(url.substring(url.lastIndexOf("/")+), bitmap);
}
/**
* 從SD卡擷取圖檔
* @param imageName
* @return
*/
public Bitmap getBitmapFromSD(String imageName){
Bitmap bitmap = null;
File fileDir = new File(CACHEPATH);
if(fileDir.exists()){
File files[] = fileDir.listFiles();
if(files != null){
for (int i = ; i < files.length; i++) {
if((files[i].getName()).equals(imageName)){
bitmap = BitmapFactory. decodeFile(CACHEPATH
+"/"+imageName);
break;
}
}
}
}
return bitmap;
}
/**
* 儲存資料到sd卡
* @param name
* @param bitmap
*/
public void saveBitmapToSD(final String name,final Bitmap bitmap){
// 開啟線程寫入SD卡
new Thread(new Runnable() {
@Override
public void run() {
// 圖檔緩存目錄
File fileDir = new File(CACHEPATH);
// 不存在就建立該目錄
if(!fileDir.exists()){
fileDir.mkdir();
}
File fileImage = new File(fileDir, name);
FileOutputStream out = null;
try {
out = new FileOutputStream(fileImage);
bitmap.compress(Bitmap.CompressFormat.PNG, , out);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
源碼下載下傳