从设计到实现,一步步教你实现Android

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

Download

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

{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;}(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 作为默认选项呢?

漫无目的的生活就像出海航行而没有指南针

从设计到实现,一步步教你实现Android

相关文章:

你感兴趣的文章:

标签云: