文章出處

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

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

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類的構造函數中(如下所示)

 

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。

接下來,我們分析一下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》。

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

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


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()