詳細解讀Volley(三)—— ImageLoader & NetworkImageView


ImageLoader是一個加載網絡圖片的封裝類,其內部還是由ImageRequest來實現的。但因為源碼中沒有提供磁盤緩存的設置,所以咱們還需要去源碼中進行修改,讓我們可以更加自如的設定是否進行磁盤緩存。

 

一、添加對磁盤緩存的控制

我們默默的打開源碼,添加如下代碼:

    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);

 

二、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緩存,其實緩存分為L1、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將不可用。

 

三、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_widthlayout_height都設置成wrap_content就可以了,這樣它就會將該圖片的原始大小展示出來,不會進行任何壓縮。

 

參考自:

http://blog.csdn.net/guolin_blog/article/details/17482165


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM