从设计到实现,一步步教你实现Android-Universal-ImageLoader-辅助类
通过前面几篇博文。我们分析了 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-辅助类的更多相关文章
- 一步步教你轻松学关联规则Apriori算法
一步步教你轻松学关联规则Apriori算法 (白宁超 2018年10月22日09:51:05) 摘要:先验算法(Apriori Algorithm)是关联规则学习的经典算法之一,常常应用在商业等诸多领 ...
- 一步步教你搭建VS环境下用C#写WebDriver脚本
一步步教你搭建VS环境下用C#写WebDriver脚本http://www.automationqa.com/forum.php?mod=viewthread&tid=3529&fro ...
- 我写了个教程《一步步教你把ubuntu安装到U盘》
一步步教你把ubuntu安装到U盘 作者 Val 2452013147@qq.com 原因: 由于某些原因(学生党),需要把ubuntu安装到U盘到处走,百度了一下,教程都不是很好,要么很复杂,要么不 ...
- 一步步教你读懂NET中IL(附带图)
一步步教你读懂NET中IL(附带图) 接触NET也有1年左右的时间了,NET的内部实现对我产生了很大的吸引力,在msdn上找到一篇关于NET的IL代码的图解说明,写的挺不错的.个人觉得:能对这些底部的 ...
- 一步步教你轻松学奇异值分解SVD降维算法
一步步教你轻松学奇异值分解SVD降维算法 (白宁超 2018年10月24日09:04:56 ) 摘要:奇异值分解(singular value decomposition)是线性代数中一种重要的矩阵分 ...
- 一步步教你轻松学支持向量机SVM算法之案例篇2
一步步教你轻松学支持向量机SVM算法之案例篇2 (白宁超 2018年10月22日10:09:07) 摘要:支持向量机即SVM(Support Vector Machine) ,是一种监督学习算法,属于 ...
- 一步步教你轻松学支持向量机SVM算法之理论篇1
一步步教你轻松学支持向量机SVM算法之理论篇1 (白宁超 2018年10月22日10:03:35) 摘要:支持向量机即SVM(Support Vector Machine) ,是一种监督学习算法,属于 ...
- 一步步教你轻松学主成分分析PCA降维算法
一步步教你轻松学主成分分析PCA降维算法 (白宁超 2018年10月22日10:14:18) 摘要:主成分分析(英语:Principal components analysis,PCA)是一种分析.简 ...
- 一步步教你轻松学K-means聚类算法
一步步教你轻松学K-means聚类算法(白宁超 2018年9月13日09:10:33) 导读:k-均值算法(英文:k-means clustering),属于比较常用的算法之一,文本首先介绍聚类的理 ...
- 一步步教你轻松学朴素贝叶斯模型算法Sklearn深度篇3
一步步教你轻松学朴素贝叶斯深度篇3(白宁超 2018年9月4日14:18:14) 导读:朴素贝叶斯模型是机器学习常用的模型算法之一,其在文本分类方面简单易行,且取得不错的分类效果.所以很受欢迎,对 ...
随机推荐
- LintCode-赋值运算符重载
实现赋值运算符重载函数.确保: 新的数据可准确地被复制 旧的数据可准确地删除/释放 可进行 A = B = C 赋值 您在真实的面试中是否遇到过这个题? Yes 例子 假设进行 A = B 赋值.则 ...
- 1355: [Baltic2009]Radio Transmission
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 958 Solved: 659[Submit][Status][Discuss] Description ...
- Android属性动画-Interpolator和ViewPropertyAnimator的用法
Interpolator的用法 Interpolator这个东西很难进行翻译,直译过来的话是补间器的意思,它的主要作用是可以控制动画的变化速率,比如去实现一种非线性运动的动画效果.那么什么叫做非线性运 ...
- PHP万能的连接数据库
<?php class DB{ const HOST='127.0.0.1'; const USER='root'; const PASS='root'; const DATA='mooc'; ...
- Debian9.5下ssh密钥登录配置步骤(免密码登录)和ssh-keygen 命令常用参数
密钥登录步骤(免密码登录)ssh登录提供两种认证方式:口令(密码)认证方式和密钥认证方式.其中口令(密码)认证方式是我们最常用的一种,出于安全方面的考虑,介绍密钥认证方式登录到linux/unix的方 ...
- git rebase 的使用 (用于撤销某次commit)
Q: I wrote the wrong thing in a commit message. Alternatively, I've forgotten to include some files. ...
- vue 退出动画无效解决方法
遇到一个问题:给模态框加入退出动画,进入的动画效果是有的,可是退出的动画就没有了. 写个简单的结构: <div class="mask" v-show="warni ...
- shell中处理用户输入
1.使用命令行参数 在shell执行的时候命令行中输入的所有参数可以赋值给一些特殊变量,这些变量成为位置变量参数. 包括: $0返回脚本名称.$1为第一个参数.$2为第二个参数 ...$9第九个参数 ...
- yes---重复输出指定的字符串
yes命令在命令行中输出指定的字符串,直到yes进程被杀死.不带任何参数输入yes命令默认的字符串就是y. 语法 yes(参数) 参数 字符串:指定要重复打印的字符串. 实例 [root@localh ...
- 【Codeforces Round #462 (Div. 1) B】A Determined Cleanup
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 设\(设f(x)=a_d*x^{d}+a_{d-1}*x^{d-1}+...+a_1*x+a_0\) 用它去除x+k 用多项式除法除 ...