码迷,mamicode.com
首页 > 移动开发 > 详细

从设计到实现,一步步教你实现Android-Universal-ImageLoader-辅助类

时间:2015-08-17 14:07:16      阅读:156      评论:0      收藏:0      [点我收藏+]

标签:android

通过前面几篇博文,我们分析了 AUI 的缓存、工具类、显示与加载这几个方面的代码,今天呢,我们继续研究 AUI 的源码,学习其中的核心辅助工具类。希望大家能在里面学到东西哈。

Download

要下载一张图片,我们想象需要什么哈:首先我们得设定支持的协议,得有下载的链接,由相应的下载链接去下载图片,进而得到图片的输入流,剩下的就交给图片的显示/加载类处理啦。那么我们可以设计出下面的接口:

public interface ImageDownloader {

    InputStream getStream(String imageUri, Object extra) throws IOException;

    public enum Scheme {
        HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");

        private String scheme;
        private String uriPrefix;

        Scheme(String scheme) {
            this.scheme = scheme;
            uriPrefix = scheme + "://";
        }

        public static Scheme ofUri(String uri) {
            if (uri != null) {
                for (Scheme s : values()) {
                    if (s.belongsTo(uri)) {
                        return s;
                    }
                }
            }
            return UNKNOWN;
        }

        private boolean belongsTo(String uri) {
            return uri.toLowerCase(Locale.US).startsWith(uriPrefix);
        }

        public String wrap(String path) {
            return uriPrefix + path;
        }

        public String crop(String uri) {
            if (!belongsTo(uri)) {
                throw new IllegalArgumentException(String.format("URI [%1$s] doesn‘t have expected scheme [%2$s]", uri, scheme));
            }
            return uri.substring(uriPrefix.length());
        }
    }
}

看到这里大家可能会疑惑了,我们不是设计接口么,为啥要搞个枚举类型,而且还在里面搞那么多乱七八糟的东西……事实上,如果大家有看过《Thinking In Java》的话就会知道,在 Java 中,enum 实际上就是一个类,因为 enum 定义后的枚举类在编译时默认继承 java.lang.Enum 类,而该枚举类会自动被加上 final 关键字修饰,这也使得枚举类无法被继承。更详细的解释大家可以自行 Google 哈。

那么为什么要在 ImageDownloader 里引入这个枚举类呢?我们不妨先看看枚举类内到底有什么,在枚举类 Scheme 中,主要有四个方法,而这四个方法都用于处理 Uri,如:裁减 Uri、添加 Uri 前缀、判断 Uri。也就是说,Scheme 的抽象职责是:对 Uri 进行修饰。而 Scheme 对 Uri 的修饰结果将交给 ImageDownloader 完成下载操作。

换言之,Scheme 的抽象与 ImageDownloader 的抽象实际上是不一致的(ImageDownloader 的抽象是下载图片,而下载图片所需的 Uri 需要进行什么处理才能被 ImageDownloader 使用,并完成下载其实不重要),为了让降低类的耦合度,我们在 ImageDownloader 的内部实现了 Scheme 类。

那么有人可能会问了,那我们另外创建一个类不行么?就我的理解来看,肯定是可以的。因为 Scheme 本质上也是一个类,我们新创建一个类,声明为 final 类,添加相应的静态常量、方法,其实效果也是一样的。那作者为什么要这么干呢?看过《Effective Java》的朋友可能会知道,实现单例的最佳方法就是使用 enum,具体的解释大家自己去查吧,我就不在这里多说了。那么作者在这里实际上就是在接口内部定义了一个单例。

What is an efficient way to implement a singleton pattern in Java?

BaseImageDownloader 实现了 ImageDownloader 接口,而且根据我们的需求实现了相应的图片下载细节,具体没什么好讲解的,大家可以自行阅读源码哈~

Listener

在 AUI 中,实际上需要用到的 Listener 并不多,毕竟图片加载只是一个很小的功能模块嘛。那么 AUI 到底包含了什么 Listener 呢?

  • 图片加载监听器:监听图片加载的开始、结束、失败、取消
  • 图片加载进度监听器:监听图片加载的进度
  • 图片加载滚动监听器:当图片加载正在进行,若发生滚动,则停止加载,换言之,只加载当前屏幕显示的图片,图片滚动过程的图片则不加载。

可能有人会觉得很奇怪,为什么图片加载监听器和图片加载进度监听器要分开实现。其实我也想不懂,希望有人能给我个解释……

Assist

在 assist 里面有几个类我们在之前的博文中已经有提过了,我就不再这重复拉。比较简单的类我也会一笔带过,希望大家理解哈。

deque

在这里面都是一些双端队列,例如 LinkedBlockingDeque、LIFOLinkedBlockingDeque。双端队列的相应知识,以及具体实现我相信不用我在这里废话了,毕竟数据结构的课程中一定会讲到这个知识点,这也是个基本的、必须掌握的数据结构。

那么在这里我们需要了解什么呢?那就是:为什么引入双端队列作为 AUI 的数据结构,双端队列较之其他数据结构在这个应用场景下有什么优点。

我们不妨先看看 Deque 在哪里被用到吧,在 AUI 库中搜索发现,DefaultConfigurationFactory 调用了 Deque。那么 DefaultConfigurationFactory 到底是什么呢?

从该类的命名以及内部的方法名我们可以知道,DefaultConfigurationFactory 就是 AUI 默认的配置工厂类,如果开发者没有自定义相应的配置选项的话,AUI 就会使用这个类所设置的默认选项,完成相应的加载、下载、缓存等等……

不妨看看下面的代码段:

    public static Executor createExecutor(int threadPoolSize, int threadPriority,
            QueueProcessingType tasksProcessingType) {
        boolean lifo = tasksProcessingType == QueueProcessingType.LIFO;
        BlockingQueue<Runnable> taskQueue =
                lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>();
        return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue,
                createThreadFactory(threadPriority, "uil-pool-"));
    }

在这段代码中,我们会获得线程池,并且用 LIFOLinkedBlockingDeque 作为线程的处理队列。也就是说,在 AUI 库中,双端队列这个数据结构是用来完成 AUI 线程处理的。那么为什么要选择双端队列,为什么又选择 LIFOLinkedBlockingDeque 作为默认选项呢?

之所以选择双端队列,是因为 ThreadPoolExecutor 使用的是生产者-消费者模式,在 ThreadPoolExecutor 类内部使用 BlockingQueue 作为任务处理队列能满足生产者-消费者模式对任务存储数据结构的要求。而在我们的 assist 中,LIFOLinkedBlockingDeque 是 LinkedBlockingDeque 的子类,而 LinkedBlockingDeque 实现了 BlockingDeque 接口,BlockingDeque 接口又继承于 BlockingQueue。

Others

在 assist 中剩下的辅助类我觉得都挺简单的,都是一些基本 API 调用的简化,大家自己看看源码都能看懂的~

版权声明:本文为博主原创文章,未经博主允许不得转载。

从设计到实现,一步步教你实现Android-Universal-ImageLoader-辅助类

标签:android

原文地址:http://blog.csdn.net/u012403246/article/details/47723365

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!