universal image loader在listview/gridview中滾動時重復加載圖片的問題及解決方法


在listview/gridview中使用UIL來display每個item的圖片,當圖片數量較多需要滑動滾動時會出現卡頓,而且加載過的圖片再次上翻后依然會重復加載(顯示設置好的加載中圖片)

最近在使用UIL遇到了這個問題,相信這個問題許多使用UIL的人都碰到過

現在把解決方法貼出來給有同樣問題的朋友做參考

先看下UIL的工作流程

在已經允許內存,存儲卡緩存的前提下,當一個圖片被請求display時,首先要判斷圖片是否緩存在內存中,如果false則嘗試從存儲卡讀取,如果依然不存在最后才從網絡地址下載

從內存讀取的速度最快,存儲卡次之,在我們滾動listview的時候,如果是從內存加載圖片則會顯得非常流暢,如果是存儲卡就會先出現載入中圖片然后再顯示實際圖片

我們通常認為已經讀過一次的圖片自然將會加入內存緩存中,那么下一次讀取將是直接從內存中讀取,但是實際上載入過的圖片在滑動出屏幕再滑動回來后依然會再次從存儲卡讀取,這主要是UIL的緩存策略引起的一個"疑似BUG"

查看UIL的源碼,displayImage函數

    public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
        displayImage(uri, new ImageViewAware(imageView), options, null, null);
    }

會有ImageAware接口的一個實例化,這個默認的實例化有個重要的參數 :checkActualViewSize 具體說明如下

    public ViewAware(View view) {
        this(view, true);
    }

    /**
     * Constructor
     *
     * @param view                {@link android.view.View View} to work with
     * @param checkActualViewSize <b>true</b> - then {@link #getWidth()} and {@link #getHeight()} will check actual
     *                            size of View. It can cause known issues like
     *                            <a href="https://github.com/nostra13/Android-Universal-Image-Loader/issues/376">this</a>.
     *                            But it helps to save memory because memory cache keeps bitmaps of actual (less in
     *                            general) size.
     *                            <p/>
     *                            <b>false</b> - then {@link #getWidth()} and {@link #getHeight()} will <b>NOT</b>
     *                            consider actual size of View, just layout parameters. <br /> If you set 'false'
     *                            it's recommended 'android:layout_width' and 'android:layout_height' (or
     *                            'android:maxWidth' and 'android:maxHeight') are set with concrete values. It helps to
     *                            save memory.
     */
    public ViewAware(View view, boolean checkActualViewSize) {
        if (view == null) throw new IllegalArgumentException("view must not be null");

        this.viewRef = new WeakReference<View>(view);
        this.checkActualViewSize = checkActualViewSize;
    }

這個參數會影響緩存時的key名稱,當圖片第一次緩存時,當時圖片並未下載,自然無法獲得圖片的長寬尺寸,這時UIL會使用配置預設的maxwidth和maxheight為長寬,緩存的key名稱為類似這樣:url_widthxheight

在checkActualViewSize設置為true時,第二次載入圖片的view將會讀取view的長寬,這時的長寬會是圖片的實際尺寸,相應的生成的緩存key名稱也會變成url_realwidthxrealheight,這個名稱同之前緩存的不同,因此也當然不能在緩存查詢中命中

所以最后就需要再次從存儲中加載圖片,並以新的keyname再存一份副本到內存中

解決方法有兩個:

1)設置imageview的layout_width和layout_height為實際圖片長寬(假如你的圖片都是固定尺寸的,這樣做就OK了)

2)display的方法修改一下,不直接display imageview改為ImageAware,類似

ImageAware imageAware = new ImageViewAware(imageView, false);
imageLoader.displayImage(imageUri, imageAware);

顯式的將checkActualViewSize設為false, 這樣圖片的緩存也將只會保存一個副本,保證第二次查詢時可以直接命中

 

按上述方法設置之后一般來說在listview/gridview滑動時圖片效果基本沒什么大問題了,但是還有些額外設置也許也是大家需要注意的

首先是config的初始化

File cacheDir = StorageUtils.getOwnCacheDirectory(getApplicationContext(), "imageloader/Cache"); //緩存文件的存放地址
ImageLoaderConfiguration config = new ImageLoaderConfiguration 
.Builder(getApplicationContext()) 
.memoryCacheExtraOptions(480, 800) // max width, max height 
.threadPoolSize(3)//線程池內加載的數量 
.threadPriority(Thread.NORM_PRIORITY - 2)  //降低線程的優先級保證主UI線程不受太大影響
.denyCacheImageMultipleSizesInMemory() 
.memoryCache(new LruMemoryCache(5 * 1024 * 1024)) //建議內存設在5-10M,可以有比較好的表現
.memoryCacheSize(5 * 1024 * 1024) 
.discCacheSize(50 * 1024 * 1024) 
.discCacheFileNameGenerator(new Md5FileNameGenerator()) 
.tasksProcessingOrder(QueueProcessingType.LIFO) 
.discCacheFileCount(100) //緩存的文件數量 
.discCache(new UnlimitedDiscCache(cacheDir)) 
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) 
.imageDownloader(new BaseImageDownloader(getApplicationContext(), 5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)
.writeDebugLogs() // Remove for release app 
.build();

然后是option的設置

options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.default_cover)
.showImageForEmptyUri(R.drawable.default_cover)
.showImageOnFail(R.drawable.default_cover)
.cacheInMemory(true)
.cacheOnDisc(true)
.imageScaleType(ImageScaleType.NONE)
.bitmapConfig(Bitmap.Config.RGB_565)//設置為RGB565比起默認的ARGB_8888要節省大量的內存
.delayBeforeLoading(100)//載入圖片前稍做延時可以提高整體滑動的流暢度
.build();

 

 滑動時禁止加載也可以有效的提高表現

setOnScrollListener(new PauseOnScrollListener(imageLoader, true, true));//兩個分別表示拖動下拉條和滑動過程中暫停加載

 

最后就是在getview中,例行的viewholder保存狀態之外,將URL存入imageview的tag中,通過對比URL值來減少UIL的display次數以提高表現

 

UIL是個非常不錯的圖片加載類的第三方庫,可以幫我們在開發過程中省不少事,不過如果想用好也需要自己真正的去研究下

 

參考資料:

https://github.com/nostra13/Android-Universal-Image-Loader/issues/376

https://github.com/nostra13/Android-Universal-Image-Loader/wiki/Task-flow


免責聲明!

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



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