Universal_Image_Loader源碼解析

UIL是一個(gè)老牌的開源圖片加載框架,前幾年做Android開發(fā)的小伙伴肯定都非常熟悉忍级。雖然現(xiàn)在UIL的作者已經(jīng)停止進(jìn)行代碼維護(hù)失球,而且后面又有很多優(yōu)秀的開源圖片加載框架推出,比如Glide霸奕、Picasso溜宽、Fresco等。但是這并不妨礙我們對于UIL的基本使用质帅。今天就帶著大家一起來從源碼的角度來認(rèn)識一下Universal_Image_Loader适揉。

一、初識UIL

UIL的基本特性煤惩,這里簡單羅列一下:

  1. 多線程下載圖片嫉嘀,圖片可以來源于網(wǎng)絡(luò),文件系統(tǒng)魄揉,項(xiàng)目文件夾assets中以及drawable中等
  2. 支持隨意的配置ImageLoader吃沪,例如線程池,圖片下載器什猖,內(nèi)存緩存策略票彪,硬盤緩存策略红淡,圖片顯示選項(xiàng)以及其他的一些配置
  3. 支持圖片的內(nèi)存緩存,文件系統(tǒng)緩存或者SD卡緩存
  4. 支持圖片下載過程的監(jiān)聽
  5. 根據(jù)控件(ImageView)的大小對Bitmap進(jìn)行裁剪降铸,減少Bitmap占用過多的內(nèi)存
  6. 較好的控制圖片的加載過程在旱,例如暫停圖片加載,重新開始加載圖片推掸,一般使用在ListView,GridView中桶蝎,滑動過程中暫停加載圖片,停止滑動的時(shí)候去加載圖片
  7. 提供在較慢的網(wǎng)絡(luò)下對圖片進(jìn)行加載

基本使用方法:

  1. 在Gradle的dependency下加入
    compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
  2. 加入權(quán)限:
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERNET" />
  3. 在全局Application中new一個(gè)ImageLoder對象谅畅,這個(gè)對象是單例模式的:
        public class MyApplication extends Application {  
           
             @Override  
             public void onCreate() {  
                 super.onCreate();  
           
                 //創(chuàng)建默認(rèn)的ImageLoader配置參數(shù)  
                ImageLoaderConfiguration configuration = ImageLoaderConfiguration  
                         .createDefault(this);  
                   
                 //進(jìn)行初始化操作
                 ImageLoader.getInstance().init(configuration);  
             }  
           
         }  

注意:

  1. 只能配置一次登渣,如多次配置,則默認(rèn)第一次的配置參數(shù)毡泻。
  2. 這里主要是imageloader的配置參數(shù)是個(gè)重點(diǎn)胜茧。
    一般用默認(rèn)配置就ok。但也提供了一些特定的設(shè)置仇味,只有真的需要的時(shí)候才用呻顽。
  3. 下面是全部配置,如果需要單獨(dú)設(shè)定某個(gè)值得時(shí)候可以使用丹墨。
    具體意思廊遍,根據(jù)方法名自己理解,或者百度贩挣。這里不再一一講解.
        File cacheDir = StorageUtils.getCacheDirectory(context);  //緩存文件夾路徑
        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
                        .memoryCacheExtraOptions(480, 800) // default = device screen dimensions 內(nèi)存緩存文件的最大長寬
                        .diskCacheExtraOptions(480, 800, null)  // 本地緩存的詳細(xì)信息(緩存的最大長寬)喉前,最好不要設(shè)置這個(gè) 
                        .taskExecutor(...)
                        .taskExecutorForCachedImages(...)
                        .threadPoolSize(3) // default  線程池內(nèi)加載的數(shù)量
                        .threadPriority(Thread.NORM_PRIORITY - 2) // default 設(shè)置當(dāng)前線程的優(yōu)先級
                        .tasksProcessingOrder(QueueProcessingType.FIFO) // default
                        .denyCacheImageMultipleSizesInMemory()
                        .memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //可以通過自己的內(nèi)存緩存實(shí)現(xiàn)
                        .memoryCacheSize(2 * 1024 * 1024)  // 內(nèi)存緩存的最大值
                        .memoryCacheSizePercentage(13) // default
                        .diskCache(new UnlimitedDiscCache(cacheDir)) // default 可以自定義緩存路徑  
                        .diskCacheSize(50 * 1024 * 1024) // 50 Mb sd卡(本地)緩存的最大值
                        .diskCacheFileCount(100)  // 可以緩存的文件數(shù)量 
                        // default為使用HASHCODE對UIL進(jìn)行加密命名, 還可以用MD5(new Md5FileNameGenerator())加密
                        .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) 
                        .imageDownloader(new BaseImageDownloader(context)) // default
                        .imageDecoder(new BaseImageDecoder()) // default
                        .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
                        .writeDebugLogs() // 打印debug log
                .build(); //開始構(gòu)建
  1. 配置加載圖片的屬性王财,這里主要是一些加載失敗的默認(rèn)圖片被饿,加載過程中的圖片,緩存位置搪搏,bitmap屬性等狭握。這里,我在使用的時(shí)候定義了三個(gè)DisplayImageOptions對象疯溺,分別是加載大圖论颅、中圖、小圖囱嫩∈逊瑁基本涵蓋了app中圖片的使用。下面給出一個(gè)小圖的options
DisplayImageOptions smallImageOptions  = new DisplayImageOptions.Builder()
            .showImageOnLoading(R.drawable.default_pic_small)
            .showImageOnFail(R.drawable.default_pic_small)
            .showImageForEmptyUri(R.drawable.default_pic_small)
            .cacheInMemory(true)
            .cacheOnDisk(true)
            .bitmapConfig(Bitmap.Config.RGB_565)
            .build();
  1. 開始加載圖片
  • 網(wǎng)絡(luò)圖片
ImageLoader.getInstance().displayImage(url, imageView, option);
  • drawable圖片(非9.png圖片)
String drawableUrl = Scheme.DRAWABLE.wrap("R.drawable.image"); 
ImageLoader.getInstance().displayImage(drawableUrl , imageView, option);
或者 
ImageLoader.getInstance().displayImage("drawable://" + imageId,
                imageView);
  • 本地圖片
String imageUrl = ImageDownloader.Scheme.FILE.wrap("/mnt/sdcard/image.png");
ImageLoader.getInstance().displayImage(imageUrl, imageView, option);
  • assets
String assetsUrl = Scheme.ASSETS.wrap("image.png");  
ImageLoader.getInstance().displayImage(assetsUrl , imageView, option);
  • Content provider
String contentprividerUrl = "content://media/external/audio/albumart/13";  
ImageLoader.getInstance().displayImage(contentprividerUrl , imageView, option);
  1. GirdView,ListView加載圖片
    當(dāng)我們快速滑動GridView墨闲,ListView今妄,我們希望能停止圖片的加載,而在GridView,ListView停止滑動的時(shí)候加載當(dāng)前界面的圖片盾鳞。我們只需要在初始化ListView和GridView的時(shí)候犬性,加上下面兩行配置:
listView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));  
gridView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));  

二、UID的圖片緩存策略

  1. 圖片緩存的概念腾仅,簡單點(diǎn)來說當(dāng)你第一次打開某個(gè)界面的時(shí)候乒裆,圖片可能要等待幾秒鐘才能加載完成,但當(dāng)你第二次進(jìn)入時(shí)推励,圖片直接就可以顯示出來鹤耍,相對于第一次加載快了很多。這里運(yùn)用的就是圖片緩存技術(shù)验辞。
  2. 三級緩存稿黄。一般的策略是先從內(nèi)存中找,沒有的話再去本地disk中找跌造,再沒有的話就從網(wǎng)絡(luò)獲取杆怕。下面的文章主要說一下內(nèi)存緩存和磁盤緩存,網(wǎng)絡(luò)緩存就是重新加載了鼻听。三級緩存的特點(diǎn):
  • 內(nèi)存緩存 優(yōu)先加載财著,速度最快
  • 本地緩存 次優(yōu)先加載 速度稍快
  • 網(wǎng)絡(luò)緩存 最后加載 速度由網(wǎng)絡(luò)速度決定(浪費(fèi)流量)

三联四、內(nèi)存緩存

  1. 內(nèi)存緩存中的強(qiáng)引用和弱引用
  • 強(qiáng)引用是指創(chuàng)建一個(gè)對象并把這個(gè)對象賦給一個(gè)引用變量撑碴, 強(qiáng)引用有引用變量指向時(shí)永遠(yuǎn)不會被垃圾回收。即使內(nèi)存不足的時(shí)候?qū)幵笀?bào)OOM也不被垃圾回收器回收朝墩,我們new的對象都是強(qiáng)引用
  • 弱引用通過weakReference類來實(shí)現(xiàn)醉拓,它具有很強(qiáng)的不確定性,如果垃圾回收器掃描到有著WeakReference的對象收苏,就會將其回收釋放內(nèi)存
  1. UIL中的內(nèi)存緩存策略
    UIL提供的內(nèi)存緩存策略有下面幾種亿卤,大家可以自己去看下源碼,非常簡單鹿霸。
    (1)使用的是強(qiáng)引用緩存
    • LruMemoryCache(這個(gè)類就是這個(gè)開源框架默認(rèn)的內(nèi)存緩存類排吴,緩存的是bitmap的強(qiáng)引用,下面我會從源碼上面分析這個(gè)類)

(2) 使用強(qiáng)引用和弱引用相結(jié)合的緩存有
- UsingFreqLimitedMemoryCache(如果緩存的圖片總量超過限定值懦鼠,先刪除使用頻率最小的bitmap)
- LRULimitedMemoryCache(這個(gè)也是使用的lru算法钻哩,和LruMemoryCache不同的是,他緩存的是bitmap的弱引用)
- FIFOLimitedMemoryCache(先進(jìn)先出的緩存策略肛冶,當(dāng)超過設(shè)定值街氢,先刪除最先加入緩存的bitmap)
- LargestLimitedMemoryCache(當(dāng)超過緩存限定值,先刪除最大的bitmap對象)
- LimitedAgeMemoryCache(當(dāng) bitmap加入緩存中的時(shí)間超過我們設(shè)定的值睦袖,將其刪除)

(3) 只使用弱引用緩存
- WeakMemoryCache(這個(gè)類緩存bitmap的總大小沒有限制珊肃,唯一不足的地方就是不穩(wěn)定,緩存的圖片容易被回收掉)

  1. UIL手動修改內(nèi)存緩存策略
    在Application中初始化ImageLoaderConfiguration的時(shí)候,加上memoryCache就可以了伦乔。
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)  
        .memoryCache(new WeakMemoryCache())  
        .build();  
  1. LruMemoryCache 類解析
    這里已LruMemoryCache 類為例進(jìn)行分析厉亏,源碼的話,就不貼了评矩。只列出關(guān)鍵的代碼叶堆。
    private final LinkedHashMap<String, Bitmap> map;  
    private final int maxSize;  
    /** Size of this cache in bytes */  
    private int size;  

LruMemoryCache 緩存了一個(gè)LinkedHashMap<String, Bitmap> map對象,用來保存圖片斥杜。同時(shí)還定義了一個(gè)最大緩存值虱颗,以及一個(gè)當(dāng)前的緩存值。

 /** 
     * Remove the eldest entries until the total of remaining entries is at or below the requested size. 
     * 
     * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements. 
     */  
    private void trimToSize(int maxSize) {  
        while (true) {  
            String key;  
            Bitmap value;  
            synchronized (this) {  
                if (size < 0 || (map.isEmpty() && size != 0)) {  
                    throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");  
                }  
  
                if (size <= maxSize || map.isEmpty()) {  
                    break;  
                }  
  
                Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();  
                if (toEvict == null) {  
                    break;  
                }  
                key = toEvict.getKey();  
                value = toEvict.getValue();  
                map.remove(key);  
                size -= sizeOf(key, value);  
            }  
        }  
    } 

如果當(dāng)前緩存的bitmap總數(shù)小于設(shè)定值maxSize蔗喂,不做任何處理忘渔,如果當(dāng)前緩存的bitmap總數(shù)大于maxSize,刪除LinkedHashMap中的第一個(gè)元素,size中減去該bitmap對應(yīng)的byte數(shù)

四缰儿、硬盤緩存

  1. UIL提供的硬盤緩存策略
  • FileCountLimitedDiscCache(可以設(shè)定緩存圖片的個(gè)數(shù)畦粮,當(dāng)超過設(shè)定值,刪除掉最先加入到硬盤的文件)
  • LimitedAgeDiscCache(設(shè)定文件存活的最長時(shí)間乖阵,當(dāng)超過這個(gè)值宣赔,就刪除該文件)
  • TotalSizeLimitedDiscCache(設(shè)定緩存bitmap的最大值,當(dāng)超過這個(gè)值瞪浸,刪除最先加入到硬盤的文件)
  • UnlimitedDiscCache(這個(gè)緩存類沒有任何的限制)

五儒将、UIL加載一張網(wǎng)絡(luò)圖片的過程

  1. 先看下我們平常使用加載圖片的代碼:
    ImageLoader.getInstance().displayImage(imageUrl, imageView, options);
  2. 再看下displayImage的源碼:
    public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
        displayImage(uri, new ImageViewAware(imageView), options, null, null);
    }

這個(gè)ImageViewAware是什么?看下源碼的注釋:
"Wrapper for Android {@link android.widget.ImageView ImageView}. Keeps weak reference of ImageView to prevent memory leaks."
將ImageView的強(qiáng)引用變成弱引用对蒲,當(dāng)內(nèi)存不足的時(shí)候钩蚊,可以更好的回收ImageView對象,還有就是獲取ImageView的寬度和高度蹈矮。使得我們可以根據(jù)ImageView的寬高去對圖片進(jìn)行一個(gè)裁剪砰逻,減少內(nèi)存的使用。

看下ImageLoader中的displayImage方法泛鸟,上代碼:

/**
     * Adds display image task to execution pool. Image will be set to ImageAware when it's turn.<br />
     * <b>NOTE:</b> {@link #init(ImageLoaderConfiguration)} method must be called before this method call
     *
     * @param uri              Image URI (i.e. "http://site.com/image.png", "file:///mnt/sdcard/image.png")
     * @param imageAware       {@linkplain com.nostra13.universalimageloader.core.imageaware.ImageAware Image aware view}
     *                         which should display image
     * @param options          {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions Options} for image
     *                         decoding and displaying. If <b>null</b> - default display image options
     *                         {@linkplain ImageLoaderConfiguration.Builder#defaultDisplayImageOptions(DisplayImageOptions)
     *                         from configuration} will be used.
     * @param listener         {@linkplain ImageLoadingListener Listener} for image loading process. Listener fires
     *                         events on UI thread if this method is called on UI thread.
     * @param progressListener {@linkplain com.nostra13.universalimageloader.core.listener.ImageLoadingProgressListener
     *                         Listener} for image loading progress. Listener fires events on UI thread if this method
     *                         is called on UI thread. Caching on disk should be enabled in
     *                         {@linkplain com.nostra13.universalimageloader.core.DisplayImageOptions options} to make
     *                         this listener work.
     * @throws IllegalStateException    if {@link #init(ImageLoaderConfiguration)} method wasn't called before
     * @throws IllegalArgumentException if passed <b>imageAware</b> is null
     */
    public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
            ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
        checkConfiguration();
        if (imageAware == null) {
            throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
        }
        if (listener == null) {
            listener = emptyListener;
        }
        if (options == null) {
            options = configuration.defaultDisplayImageOptions;
        }

        if (TextUtils.isEmpty(uri)) {
            engine.cancelDisplayTaskFor(imageAware);
            listener.onLoadingStarted(uri, imageAware.getWrappedView());
            if (options.shouldShowImageForEmptyUri()) {
                imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
            } else {
                imageAware.setImageDrawable(null);
            }
            listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
            return;
        }

        ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
        String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
        engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);

        listener.onLoadingStarted(uri, imageAware.getWrappedView());

        Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
        if (bmp != null && !bmp.isRecycled()) {
            L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);

            if (options.shouldPostProcess()) {
                ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                        options, listener, progressListener, engine.getLockForUri(uri));
                ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
                        defineHandler(options));
                if (options.isSyncLoading()) {
                    displayTask.run();
                } else {
                    engine.submit(displayTask);
                }
            } else {
                options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
                listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
            }
        } else {
            if (options.shouldShowImageOnLoading()) {
                imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
            } else if (options.isResetViewBeforeLoading()) {
                imageAware.setImageDrawable(null);
            }

            ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                    options, listener, progressListener, engine.getLockForUri(uri));
            LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
                    defineHandler(options));
            if (options.isSyncLoading()) {
                displayTask.run();
            } else {
                engine.submit(displayTask);
            }
        }
    }

我大致說下處理流程:

  1. 檢查配置是否初始化蝠咆;
  2. 檢查imageAware,也就是ImageView是否為空北滥;
  3. 檢查ImageLoadingListener和ImageLoadingProgressListener是否為空刚操,如果為空就使用默認(rèn)的配置。
  4. 判斷圖片地址碑韵。如果為空就顯示配置的空圖片赡茸,并取消網(wǎng)絡(luò)任務(wù),同時(shí)調(diào)用ImageLoadingListener的onLoadingComplete方法祝闻,結(jié)束本次加載占卧。如果圖片地址不為空遗菠,那么進(jìn)行下面的步驟
  5. 將ImageView的寬高封裝成ImageSize對象.如果獲取ImageView的寬高為0,就會使用手機(jī)屏幕的寬高作為ImageView的寬高华蜒。
  6. 從內(nèi)存中查找這個(gè)圖片辙纬。如果從內(nèi)存中找到了,那么再判斷顯示圖片的options.shouldPostProcess()是否為true叭喜。postProcessor這個(gè)屬性主要是判斷是否需要對取到的這個(gè)圖片進(jìn)行處理贺拣,比如切成圓角邊等。這個(gè)對應(yīng)的需要自己實(shí)現(xiàn)BitmapProcessor接口來處理這個(gè)bitmap捂蕴。如果需要處理圖片譬涡,就進(jìn)行處理,然后顯示啥辨。如果不需要處理涡匀,就直接顯示到imageAware上去。加載完成溉知。
  7. 如果沒有從內(nèi)存中找到這張圖片陨瘩,那么就實(shí)例化一個(gè)LoadAndDisplayImageTask對象。這個(gè)對象實(shí)現(xiàn)了Runnable,如果配置了isSyncLoading為true, 直接執(zhí)行LoadAndDisplayImageTask的run方法级乍,表示同步舌劳,默認(rèn)是false,將LoadAndDisplayImageTask提交給線程池對象。這個(gè)對象的主要作用是去disk或者網(wǎng)絡(luò)中去加載圖片玫荣。

下面看下LoadAndDisplayImageTask是如何從disk或者網(wǎng)絡(luò)加載圖片的甚淡。先看它的run方法

    @Override
    public void run() {
        if (waitIfPaused()) return;
        if (delayIfNeed()) return;

        ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
        L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
        if (loadFromUriLock.isLocked()) {
            L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
        }

        loadFromUriLock.lock();
        Bitmap bmp;
        try {
            checkTaskNotActual();

            bmp = configuration.memoryCache.get(memoryCacheKey);
            if (bmp == null || bmp.isRecycled()) {
                bmp = tryLoadBitmap();
                if (bmp == null) return; // listener callback already was fired

                checkTaskNotActual();
                checkTaskInterrupted();

                if (options.shouldPreProcess()) {
                    L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);
                    bmp = options.getPreProcessor().process(bmp);
                    if (bmp == null) {
                        L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
                    }
                }

                if (bmp != null && options.isCacheInMemory()) {
                    L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
                    configuration.memoryCache.put(memoryCacheKey, bmp);
                }
            } else {
                loadedFrom = LoadedFrom.MEMORY_CACHE;
                L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
            }

            if (bmp != null && options.shouldPostProcess()) {
                L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
                bmp = options.getPostProcessor().process(bmp);
                if (bmp == null) {
                    L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
                }
            }
            checkTaskNotActual();
            checkTaskInterrupted();
        } catch (TaskCancelledException e) {
            fireCancelEvent();
            return;
        } finally {
            loadFromUriLock.unlock();
        }

        DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
        runTask(displayBitmapTask, syncLoading, handler, engine);
    }
  1. if (waitIfPaused()) return;這句話主要是用在ListView或者GridView在滑動過程中,不去加載圖片崇决。這里需要配合
    ListView.setOnScrollListener(new PauseOnScrollListener(pauseOnScroll, pauseOnFling ))
  2. if (waitIfPaused()) return;和if (delayIfNeed()) return;的返回值最終都由一個(gè)方法isTaskNotActual決定材诽〉状欤看下這個(gè)方法的源碼
private boolean isTaskNotActual() {
        return isViewCollected() || isViewReused();
    }

isViewCollected()是判斷我們ImageView是否被垃圾回收器回收了恒傻,如果回收了,LoadAndDisplayImageTask方法的run()就直接返回了建邓,isViewReused()判斷該ImageView是否被重用盈厘,被重用run()方法也直接返回。被重用的情況主要發(fā)生在ListView和GridView當(dāng)中官边,如果當(dāng)前加載的ImageView已經(jīng)被重用了沸手,那么就沒必要繼續(xù)加載之前的圖片了,所以直接返回注簿。

  1. 檢查完可以去加載圖片了契吉。先加一個(gè)鎖ReentrantLock。這個(gè)鎖的作用就是相同url的請求只執(zhí)行一次诡渴。比如listview中的一個(gè)item需要從網(wǎng)上加載圖片捐晶。我們用手機(jī)滑動ListView讓這個(gè)item,一會滑入界面,然后滑出后再馬上滑入惑灵。重復(fù)幾次的話山上,就會有很多個(gè)相同url的請求。加了這個(gè)鎖的作用英支,這些請求只執(zhí)行一遍佩憾,而不是全部去執(zhí)行一遍。
    再看下用了這個(gè)url請求后干了什么
  2. checkTaskNotActual()干花。這個(gè)也是檢查view是否被回收和復(fù)用妄帘。
  3. 再從緩存中檢查一遍,如果緩存中沒有池凄,那么去tryLoadBitmap();這個(gè)就是加載的方法寄摆。大概的流程就是先從disk緩存中獲取圖片,如果沒有再去從網(wǎng)絡(luò)中獲取修赞,然后將bitmap保存在文件系統(tǒng)中婶恼。
  4. 從服務(wù)器上獲取圖片保存到本地是調(diào)用了tryCacheImageOnDisk這個(gè)方法。這個(gè)方法里調(diào)用了downloadImage這個(gè)方法去下載圖片柏副。
  5. 剩下的就是顯示圖片了勾邦。 同樣會檢查一遍view有沒有被回收,被復(fù)用割择。

至此眷篇,顯示圖片的過程就結(jié)束了。

筆者在寫這篇博客的過程中荔泳,從以下兩篇文章中學(xué)到了很多有價(jià)值的東西蕉饼,十分感謝!
Android 開源框架Universal-Image-Loader完全解析
Android開源框架Universal-Image-Loader詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末玛歌,一起剝皮案震驚了整個(gè)濱河市昧港,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌支子,老刑警劉巖创肥,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異值朋,居然都是意外死亡叹侄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門昨登,熙熙樓的掌柜王于貴愁眉苦臉地迎上來趾代,“玉大人,你說我怎么就攤上這事丰辣∪銮浚” “怎么了丈甸?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長尿褪。 經(jīng)常有香客問我睦擂,道長,這世上最難降的妖魔是什么杖玲? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任顿仇,我火速辦了婚禮,結(jié)果婚禮上摆马,老公的妹妹穿的比我還像新娘臼闻。我一直安慰自己,他們只是感情好囤采,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布堪旧。 她就那樣靜靜地躺著畜挥,像睡著了一般册倒。 火紅的嫁衣襯著肌膚如雪西雀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天代虾,我揣著相機(jī)與錄音进肯,去河邊找鬼。 笑死棉磨,一個(gè)胖子當(dāng)著我的面吹牛江掩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乘瓤,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼环形,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了衙傀?” 一聲冷哼從身側(cè)響起抬吟,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎差油,沒想到半個(gè)月后拗军,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體任洞,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蓄喇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了交掏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妆偏。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖盅弛,靈堂內(nèi)的尸體忽然破棺而出钱骂,到底是詐尸還是另有隱情叔锐,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布见秽,位于F島的核電站愉烙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏解取。R本人自食惡果不足惜步责,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望禀苦。 院中可真熱鬧蔓肯,春花似錦、人聲如沸振乏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽慧邮。三九已至调限,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間误澳,已是汗流浹背旧噪。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脓匿,地道東北人淘钟。 一個(gè)月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像陪毡,于是被迫代替她去往敵國和親米母。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容