從源代碼分析Android-Universal-Image-Loader圖片下載技巧

在手機上尤爲須要考慮網絡對圖片下載的影響,常見的狀況是在2G網絡、在3G網絡須要不一樣的下載策略,也就是說在慢速網絡與快速網絡中下載須要考慮不一樣的策略。一種常見的策略就是Android客戶端和服務端相配合的方式,針對慢速網絡對圖片進行優化(讓圖片的質量低一點,保證能下載),可是這種狀況不在本文討論的範圍中。在本文中主要討論針對不能改變的服務器圖片質量(圖片的大小 xx KB),Android-Universal-Image-Loader所採起的下載策略。html

    須要具體考慮網絡狀況有:快速、慢速、無網絡權限。針對這三種狀況,在UIL中分別定義了三種策略。仍是讓咱們從代碼入手看看。在《從代碼分析Android-Universal-Image-Loader的圖片加載、顯示流程》咱們分析了圖片的下載是從LoadAndDisplayImageTask.decodeImage(…)中開始的,其中函數內部調用了getDownloader(),而後在ImageDecoder接口的實現類(BaseImageDecoder)中獲取InputStream實現圖片的下載和解析。跟進去getDownloader()中看看。java

private ImageDownloader getDownloader() {
        ImageDownloader d;
        if (engine.isNetworkDenied()) {
            d = networkDeniedDownloader;
        } else if (engine.isSlowNetwork()) {
            d = slowNetworkDownloader;
        } else {
            d = downloader;
        }
        return d;
    }

networkDeniedDownloader、slowNetworkDownloader、downloader到底是什麼?在LoadAndDisplayImageTask的構造函數中咱們看到他們實際是來源於ImageLoaderConfiguration類中對應的networkDeniedDownloader、slowNetworkDownloader、downloader。在ImageLoaderConfiguration的構造函數總,咱們發現downloader來源於ImageLoaderConfiguration.Builder,分析後發現它就是一個BaseImageDownloader對象(最後在DefaultConfigurationFactory.createImageDownloade(…)中被初始化)。回到ImageLoaderConfiguration類的構造函數中(以下所示)android

 

private ImageLoaderConfiguration(final Builder builder) {
        resources = builder.context.getResources();
        maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache;
        maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache;
        maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache;
        maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache;
        processorForDiskCache = builder.processorForDiskCache;
        taskExecutor = builder.taskExecutor;
        taskExecutorForCachedImages = builder.taskExecutorForCachedImages;
        threadPoolSize = builder.threadPoolSize;
        threadPriority = builder.threadPriority;
        tasksProcessingType = builder.tasksProcessingType;
        diskCache = builder.diskCache;
        memoryCache = builder.memoryCache;
        defaultDisplayImageOptions = builder.defaultDisplayImageOptions;
        downloader 
=
 builder.downloader;         decoder = builder.decoder;

        customExecutor = builder.customExecutor;
        customExecutorForCachedImages = builder.customExecutorForCachedImages;

        networkDeniedDownloader 
= new NetworkDeniedImageDownloader(downloader); slowNetworkDownloader = new
 SlowNetworkImageDownloader(downloader);

        L.writeDebugLogs(builder.writeLogs);
    }

咱們發現networkDeniedDownloader、slowNetworkDownloader都依賴與downloader對象,猜測這兩個類應該是對BaseImageDownloader的一個包裝。下面咱們貼出NetworkDeniedImageDownloader、SlowNetworkImageDownloader的代碼(它們在com.nostra13.universalimageloader.core.ImageLoaderConfiguration類中)服務器

/**
     * Decorator. Prevents downloads from network (throws {@link IllegalStateException exception}).<br />
     * In most cases this downloader shouldn't be used directly.
     *
     * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
     * @since 1.8.0
     */
    private static class NetworkDeniedImageDownloader implements ImageDownloader {

        private final ImageDownloader wrappedDownloader;

        public NetworkDeniedImageDownloader(ImageDownloader wrappedDownloader) {
            this.wrappedDownloader = wrappedDownloader;
        }

        @Override
        public InputStream getStream(String imageUri, Object extra) throws IOException {
            switch (Scheme.ofUri(imageUri)) {
                case HTTP:
                case HTTPS:
                    throw new IllegalStateException();
                default:
                    return wrappedDownloader.getStream(imageUri, extra);
            }
        }
    }

    /**
     * Decorator. Handles <a href="http://code.google.com/p/android/issues/detail?id=6066">this problem</a> on slow networks
     * using {@link com.nostra13.universalimageloader.core.assist.FlushedInputStream}.
     *
     * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
     * @since 1.8.1
     */
    private static class SlowNetworkImageDownloader implements ImageDownloader {

        private final ImageDownloader wrappedDownloader;

        public SlowNetworkImageDownloader(ImageDownloader wrappedDownloader) {
            this.wrappedDownloader = wrappedDownloader;
        }

        @Override
        public InputStream getStream(String imageUri, Object extra) throws IOException {
            InputStream imageStream = wrappedDownloader.getStream(imageUri, extra);
            switch (Scheme.ofUri(imageUri)) {
                case HTTP:
                case HTTPS:
                    return new FlushedInputStream(imageStream);
                default:
                    return imageStream;
            }
        }
    }

先看到NetworkDeniedImageDownloader類,這個類中因爲對應的是沒有網絡訪問權限(android.permission.INTERNET)的狀況,這種狀況下Http和Https天然就不能使用了,其餘狀況(如從本地資源中獲取圖片)仍是能夠的。NetworkDeniedImageDownloader.wrappedDownloader對象是什麼呢?其實就是咱們剛剛ImageLoaderConfiguration構造函數中傳入的BaseImageDownloader對象。在看看這個類中的getStream(…)方法。網絡

@Override
    public InputStream getStream(String imageUri, Object extra) throws IOException {
        switch (Scheme.ofUri(imageUri)) {
            case HTTP:
            case HTTPS:
                return getStreamFromNetwork(imageUri, extra);
            case FILE:
                return getStreamFromFile(imageUri, extra);
            case CONTENT:
                return getStreamFromContent(imageUri, extra);
            case ASSETS:
                return getStreamFromAssets(imageUri, extra);
            case DRAWABLE:
                return getStreamFromDrawable(imageUri, extra);
            case UNKNOWN:
            default:
                return getStreamFromOtherSource(imageUri, extra);
        }
    }

從這個函數中,咱們能夠看到UIL經過Scheme.ofUri(…)分析imageUri,根據ImageUri的類型選擇對應的方法進行處理。經過分析Scheme類,咱們發現UIL支持如下幾種圖片獲取方式HTTP, HTTPS, FILE, CONTENT, ASSETS, DRAWABLE。app

接下來,咱們分析一下SlowNetworkImageDownloader.getStream(…)方法,每一次圖片的下載最終都會經過BitmapFactory.decodeStream解析成Bitmap,供ImageView顯示。咱們能夠發現這個方法針對慢速網絡使用FlushedInputStream來處理。使用這個類的緣由是由於在慢速網絡中,BitmapFactory.decodeStream沒法正確解析完整的圖片。具體的能夠參考StackOverFlow上的帖子《BitmapFactory.decodeStream always returns null and skia decoder shows decode returned false》和一個Google上的Bug 報告《BitmapFactory.decodeStream() fails if InputStream.skip() does not skip fully》。eclipse

網速不慢的下載就直接使用BaseImageDownloader.getStream(…)方法了。ide

至此,咱們已經分析了UIL中圖片下載技巧,最後梳理一下。爲了應對慢速、正常、訪問受限網絡,UIL分別 使用了SlowNetworkDownloader、BaseImageLoader、NetworkDeniedDownloader來應對這些策略,在LoadAndDisplayImageTask.getDownloader(…)中經過獲取對應的downloader,最後經過LoadAndDisplayImageTask.decodeImage(…)將圖片解析出來。函數

相關文章
相關標籤/搜索