ImageLoader是一個加載網絡圖片的封裝類,其內部仍是由ImageRequest來實現的。但由於源碼中沒有提供磁盤緩存的設置,因此我們還須要去源碼中進行修改,讓咱們能夠更加自如的設定是否進行磁盤緩存。html
如 果你以爲ImageRequest已經很是好用了,那我只能說你太容易知足了 ^_^。實際上,Volley在請求網絡圖片方面能夠作到的還遠遠不止這些,而ImageLoader就是一個很好的例子。ImageLoader也能夠 用於加載網絡上的圖片,而且它的內部也是使用ImageRequest來實現的,不過ImageLoader明顯要比ImageRequest更加高效, 由於它不只能夠幫咱們對圖片進行緩存,還能夠過濾掉重複的連接,避免重複發送請求。android
因爲ImageLoader已經不是繼承自Request的了,因此它的用法也和咱們以前學到的內容有所不一樣,總結起來大體能夠分爲如下四步:緩存
1. 建立一個RequestQueue對象。網絡
2. 建立一個ImageLoader對象。ide
3. 獲取一個ImageListener對象。函數
4. 調用ImageLoader的get()方法加載網絡上的圖片。佈局
下面咱們就來按照這個步驟,學習一下ImageLoader的用法吧。首先第一步的建立RequestQueue對象咱們已經寫過不少遍了,相信已經不用再重複介紹了,那麼就從第二步開始學習吧,新建一個ImageLoader對象,代碼以下所示:post
ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() { @Override public void putBitmap(String url, Bitmap bitmap) { } @Override public Bitmap getBitmap(String url) { return null; } });
能夠看到,ImageLoader的構造函數接收兩個參數,第一個參數就是RequestQueue對象,第二個參數是一個ImageCache對象,這裏咱們先new出一個空的ImageCache的實現便可。性能
接下來須要獲取一個ImageListener對象,代碼以下所示:學習
ImageListener listener = ImageLoader.getImageListener(imageView,
R.drawable.default_image, R.drawable.failed_image);
我 們經過調用ImageLoader的getImageListener()方法可以獲取到一個ImageListener對 象,getImageListener()方法接收三個參數,第一個參數指定用於顯示圖片的ImageView控件,第二個參數指定加載圖片的過程當中顯示 的圖片,第三個參數指定加載圖片失敗的狀況下顯示的圖片。
最後,調用ImageLoader的get()方法來加載圖片,代碼以下所示:
imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg", listener);
get()方法接收兩個參數,第一個參數就是圖片的URL地址,第二個參數則是剛剛獲取到的ImageListener對象。固然,若是你想對圖片的大小進行限制,也可使用get()方法的重載,指定圖片容許的最大寬度和高度,以下所示:
imageLoader.get("http://img.my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",
listener, 200, 200);
如今運行一下程序並開始加載圖片,你將看到ImageView中會先顯示一張默認的圖片,等到網絡上的圖片加載完成後,ImageView則會自動顯示該圖,效果以下圖所示。
雖然如今咱們已經掌握了ImageLoader的用法,可是剛纔介紹的ImageLoader的優勢卻尚未使用到。爲何呢?由於這裏建立的 ImageCache對象是一個空的實現,徹底沒能起到圖片緩存的做用。其實寫一個ImageCache也很是簡單,可是若是想要寫一個性能很是好的 ImageCache,最好就要藉助Android提供的LruCache功能了,若是你對LruCache還不瞭解,能夠參考我以前的一篇博客Android高效加載大圖、多圖解決方案,有效避免程序OOM。
這裏咱們新建一個BitmapCache並實現了ImageCache接口,以下所示:
public class BitmapCache implements ImageCache { private LruCache<String, Bitmap> mCache; public BitmapCache() { int maxSize = 10 * 1024 * 1024; mCache = new LruCache<String, Bitmap>(maxSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getRowBytes() * bitmap.getHeight(); } }; } @Override public Bitmap getBitmap(String url) { return mCache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } }
能夠看到,這裏咱們將緩存圖片的大小設置爲10M。接着修改建立ImageLoader實例的代碼,第二個參數傳入BitmapCache的實例,以下所示:
ImageLoader imageLoader = new ImageLoader(mQueue, new BitmapCache());
這樣咱們就把ImageLoader的功能優點充分利用起來了
1、添加對磁盤緩存的控制
咱們默默的打開源碼,添加以下代碼:
private boolean mShouldCache = true; /** * Set whether or not responses to this request should be cached(Disk Cache). * * @return This Request object to allow for chaining. */ public void setShouldCache(boolean shouldCache) { mShouldCache = shouldCache; } /** * Returns true if responses to this request should be cached. */ public final boolean shouldCache() { return mShouldCache; }
定位到get方法
public ImageContainer get(String requestUrl, ImageListener imageListener, int maxWidth, int maxHeight)
找到初始化Request<Bitmap>的地方。
public ImageContainer get(String requestUrl, ImageListener imageListener, int maxWidth, int maxHeight) { // only fulfill requests that were initiated from the main thread. throwIfNotOnMainThread(); final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight); // Try to look up the request in the cache of remote images. Bitmap cachedBitmap = mCache.getBitmap(cacheKey); if (cachedBitmap != null) { // Return the cached bitmap. ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null); imageListener.onResponse(container, true); return container; } // The bitmap did not exist in the cache, fetch it! ImageContainer imageContainer = new ImageContainer(null, requestUrl, cacheKey, imageListener); // Update the caller to let them know that they should use the default bitmap. imageListener.onResponse(imageContainer, true); // Check to see if a request is already in-flight. BatchedImageRequest request = mInFlightRequests.get(cacheKey); if (request != null) { // If it is, add this request to the list of listeners. request.addContainer(imageContainer); return imageContainer; } // The request is not already in flight. Send the new request to the network and // track it. Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey); mRequestQueue.add(newRequest);
mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer)); return imageContainer; }
把紅色代碼中間添加:newRequest.setShouldCache(mShouldCache);最終效果以下:
// The request is not already in flight. Send the new request to the network and // track it. Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, cacheKey); newRequest.setShouldCache(mShouldCache); mRequestQueue.add(newRequest);
2、ImageLoader
/** * Constructs a new ImageLoader. * @param queue The RequestQueue to use for making image requests. * @param imageCache The cache to use as an L1 cache. */ public ImageLoader(RequestQueue queue, ImageCache imageCache) { mRequestQueue = queue; mCache = imageCache; }
初始化要傳入兩個參數:①RequestQueue對象;②ImageCache對象(不能傳null!!!!)
RequestQueue這個我就很少說了,以前的文章已經講解過了,下面來講說ImageCache這個對象。
2.1 創建ImageCache對象來實現內存緩存
ImageCache是一個圖片的內存緩存對象,源碼中叫作L1緩存,其實緩存分爲L一、L2兩種,L1就是所謂的內存緩存,將展現過的圖片放入內存中進行緩存,L2就是磁盤緩存,若是這個圖片下載完成,它能夠被存放到磁盤中,在沒有網絡的時候就能夠調出來使用了。
爲了簡單我先空實現ImageCache接口,產生一個MyImageCache對象
class MyImageCache implements ImageCache { @Override public Bitmap getBitmap(String url) { return null; } @Override public void putBitmap(String url, Bitmap bitmap) { } }
這個接口提供的方法簡單明瞭,從此咱們能夠用本身的內存緩存來完善這個類,當前getBitmap返回的是null,說明這個內存緩存沒啥用處,和沒緩存同樣。
2.2 實現加載網絡圖片
ImageLoader imageLoader = new ImageLoader(mQueue, new MyImageCache()); ImageListener listener = ImageLoader.getImageListener(iv, R.drawable.default_photo, R.drawable.error_photo); imageLoader.setShouldCache(true); imageLoader.get("http://img5.duitang.com/uploads/item/201409/14/20140914162144_MBEmX.jpeg", listener);
代碼的思路是產生ImageLoader後,再初始化一個監聽器,監聽器中傳入imageview對象,還有默認的圖片,出錯時展現的圖片,這個很好理解。最後在imageLoader的get方法中傳入URL,還有監聽器對象便可。
值得注意的是,get方法還有一種變體:
imageLoader.get("http://img5.duitang.com/uploads/item/201409/14/20140914162144_MBEmX.jpeg", listener, 0 ,0);
這裏最後傳入的數值是獲得圖片的最大寬、高,其意義和ImageRequest中的寬、高徹底一致,能夠參考以前的文章。其實,若是你去源碼中找找的話,你會發現這兩個參數最終都是傳給ImageRequest的,因此在此就不作過多講解了。
2.3 設置緩存
由於咱們一上來就修改了源碼,因此當咱們在執行get()方法前能夠經過setShouldCache(false)來取消磁盤緩存,若是你不進行設置的話默認是執行磁盤緩存的。那麼如何配置L1緩存呢?剛剛咱們的MyImageCache僅僅是一個空實現,如今就開始來完善它。
個人想法是經過LruCache進行圖片緩存,分配的緩存空間是5m。若是對LruCache不是很瞭解,能夠看看我以前的文章:詳細解讀LruCache類
class MyImageCache implements ImageCache { private LruCache<String, Bitmap> mCache; public MyImageCache() { int maxSize = 5 * 1024 * 1024; mCache = new LruCache<String, Bitmap>(maxSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getRowBytes() * bitmap.getHeight(); } }; } @Override public Bitmap getBitmap(String url) { return mCache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { mCache.put(url, bitmap); } }
每次執行get方法時,Volley會到MyImageCache中調用getBitmap(),看看有沒有內存緩存,若是你返回了null,那麼Volley就會從網絡上下載,若是不爲null,Volley會直接把取得的bitmap展現到imageview中。當圖片展現到屏幕上後(不管是這個圖片是從內存中讀的,仍是從磁盤中讀的,或者是從網絡上下載的),Volley都會自動調用putBitmap,把圖片放入內存中緩存起來。
說明:緩存的size是:bitmap.getRowBytes() * bitmap.getHeight(),這裏getRowBytes()是返回圖片每行的字節數,圖片的size應該乘以高度。
注意:imageLoader.setShouldCache(false);僅僅是設置了不實用磁盤緩存,和內存緩存沒有任何關係。若是你想要不實用內存緩存,請在自定義的ImageCache中進行處理。
2.4 其餘方法
public final boolean shouldCache()
查看是否已經作了磁盤緩存。
void setShouldCache(boolean shouldCache)
設置是否運行磁盤緩存,此方法須要在get方法前使用
public boolean isCached(String requestUrl, int maxWidth, int maxHeight)
判斷對象是否已經被緩存,傳入url,還有圖片的最大寬高
public void setBatchedResponseDelay(int newBatchedResponseDelayMs)
Sets the amount of time to wait after the first response arrives before delivering all responses. Batching can be disabled entirely by passing in 0.
設置第一次響應到達後到分發全部響應以前的總體時間,單位ms,若是你設置的時間是0,那麼Batching將不可用。
3、NetworkImageView
NetworkImageView繼承自ImageView,你能夠認爲它是一個能夠實現加載網絡圖片的imageview,十分簡單好用。這個控件在被從父控件分離的時候,會自動取消網絡請求的,即徹底不用咱們擔憂相關網絡請求的生命週期問題。
3.1 XML
<com.android.volley.toolbox.NetworkImageView android:id="@+id/network_image_view" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center_horizontal" />
3.2 JAVA
NetworkImageView networkImageView = (NetworkImageView) findViewById(R.id.network_image_view);
networkImageView.setDefaultImageResId(R.drawable.default_photo);
networkImageView.setErrorImageResId(R.drawable.error_photo);
networkImageView.setImageUrl("http://img5.duitang.com/uploads/item/201409/14/20140914162144_MBEmX.jpeg", imageLoader);
3.3 設置圖片的寬高
NetworkImageView沒有提供任何設置圖片寬高的方法,這是因爲它是一個控件,在加載圖片的時候它會自動獲取自身的寬高,而後對比網絡圖片的寬度,再決定是否須要對圖片進行壓縮。也就是說,壓縮過程是在內部徹底自動化的,並不須要咱們關心。NetworkImageView最終會始終呈現給咱們一張大小比控件尺寸略大的網絡圖片,由於它會根據控件寬高來等比縮放原始圖片,這點須要注意,若是你想要了解詳細原理,請看我以前的ImageRequest介紹。
若是你不想對圖片進行壓縮的話,只須要在佈局文件中把NetworkImageView的layout_width和layout_height都設置成wrap_content就能夠了,這樣它就會將該圖片的原始大小展現出來,不會進行任何壓縮。