zoukankan      html  css  js  c++  java
  • 从源代码分析Android-Universal-Image-Loader图片下载技巧

    在手机上尤其需要考虑网络对图片下载的影响,常见的情况是在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(…)将图片解析出来。

  • 相关阅读:
    实习第一周小记------生活不易
    第一次实习面试感受----苦逼程序员生活初体验
    第一次实习面试感受----苦逼程序员生活初体验 分类: 程序人生 2013-07-28 14:13 2395人阅读 评论(0) 收藏
    Java-基于JDK的动态代理
    JS字符串去替换元素再转换成数组
    byte溢出栗子
    Java链式写法
    Scala笔记
    Java反射使用方法
    Java数组3种创建方式
  • 原文地址:https://www.cnblogs.com/kissazi2/p/3911472.html
Copyright © 2011-2022 走看看