##碎碎念
介绍Glide的文章看了挺多,偶尔也会点击查看部分的Glide的源码,可感觉对Glide的还是不太懂。个人觉得,每个人 的水平不同,对知识点的掌握的面不同,可能他人的已掌握的知识点于自己而言是盲点,,写源码分析的人一般将自己 觉得值得写的东西记录下来,故通过看他人的二手资料来了解一个库,可能会出现这么个情况,感觉自己自己看懂了, 但却没有学到什么东西。看文章时是假设他人掌握的知识来得出结论的,可自己实际上没有学到那些前置知识。故,我 还是想尝试自己看看。
##开始
看不懂现在的库,切换早些年的版本,比较容易查看整体轮廓。
版本时间2012/12/21
1 |
|
二个缓存
加载图片的流程,感觉是最常问也最重要的知识点了。加载图片,存在二个缓存,内存缓存和磁盘缓存,空间换时间的
经典例子了。其中,缓存的实现使用是LRU算法,内部实现可以使用LinkedHashMap
,只要将构造方法中
accessOrder
设为true既可。
图片大小转换器
1 | // 有图片地址,有想要图片的大小,故我们需要将原图缩放成指定的大小 |
###关于线程切换,这里使用了Handler
的例子。
比如我们执行转换图片的任务,需要到子线程处理,转换完成后,需要将结果给UI线程来处理接口。
如何将一部分实现交给调用者,这里使用了Callback
。如下:
1 |
|
图片大小转换的具体实现
感觉缩放图片的大小具体实现挺有意思(见识短,大惊小怪)
将变换的逻辑在矩阵上操作,比如放大,设置轴点,再将该变换应用于图片流上,最后便得到我们想要的图片。
1 | public Future<Bitmap> resizeCenterCrop(final String path, final int width, final int height, ResizeCallback callback){ |
似乎是一个常见的面试问题的源头
如何加载一个很大的图片?假设一张原图片像素有1000010000,而我们只需要1010的像素的图片,那么我们不该 将整个图片读取下来再缩放成我们想要的大小,而且假设有一个巨大大图片,读取整个图片内存直接爆了。我们是能 否将部分图片读取显示呢?
大佬也遇到了这个问题,也给(google)出了答案。
其中,思路是这样的,
- 读取图片文件前半部分的字节(16字节足以),获取到该图片的信息,如图片大小
- 根据原图大小和想要的大小,算出采样的比例,再次读取是传入采样比例,这样子,读取的图片就只有目标图片 所占的内存。
1 | //from http://stackoverflow.com/questions/7051025/how-do-i-scale-a-streaming-bitmap-in-place-without-reading-the-whole-image-first |
要明白以上问题的答案,感觉应该查看这个文件。暂且了解以下两个属性吧
1 | public class BitmapFactory { |
关于图片的处理,有挺多盲区的。平常对图片精细的操作很少,诸如上述问题,日常开发App也很少遇到, 但是其原理确实应当了解。
一些零碎的版本追踪
看到作者作者一步步添加了好多小东西,
添加Presenter来将Bitmap放置到ImageView
ImageLoader可以从加载Path和Assets资源文件,抽象成公共的接口
加载图片的key,缓存使用到,key的生成从加载的大小,加入显示类型,比如center_crop,fit_center
使用Weak reference避免内存泄漏
引入android项目后的第一个版本 2013-1-10
项目使用Flickr网站的图片作为Demo
1 | //Activity中的一个ListView中的Adapter实现 |
看看如何优先从磁盘加载的呢
1 | public void downloadPhoto(Photo photo, File cacheDir, final PhotoCallback cb) { |
之后,我们在看看Downloader是如何加载网络的资源,即download方法,我们找到了Downloader
1 | public class Downloader { |
Downloader实现了如何从一个URL转变成一个文件,其中,
本身作为单例存在,因为我们对于不同的下载,我们是可以复用同一个线程池的。
而使用线程池应付多个图片的请求。
另外使用了handler在下载完成后切换到UI线程中执行会回调,回调就是执行展示的操作
回到Activiy,我们不能忘了我们的主角ImageLoader
啊
1 |
|
其中ImageLoader主要逻辑在第一版本已经说得差不多了,代码逻辑与最差版本相差不是很大。
1 | private final Handler mainHandler; |
Glide名字出生了 2013-7-17
伴随着项目包名换成glide
,不久之后,引入了Glide这个类,普通用户只需要懂得这个类就ok了,想了解原理的人
也可以从这个类开始探索~
看看作者的介绍吧
1 | /** |
期间作者改了好多次类名哦,并加了很多注释
对于线程安全方面,一些关键方法加了syschronized关键字,或是改用了concurrent包中的集合
在加载图片的公开方法上,出现了大致的轮廓,如下
1 | Glide.load(current) |
使用Vollery in UrlLoader
添加一些测试
为了方便,图片源添加一个StringLoader
,其内部是转换成UrlLoader
对于UrlLoader,因为最终都是要打开流的,故存在了StreamLoader接口
其功能就是打开流,并定义了Callback,用于打开成功或失败的处理
1 | public interface StreamLoader { |
那么图片流的来源可分为两类,本地的和远程的
本地的指Android定义流,如Assert读取的文件,提供的协议头 content:file:
远程的包括http和https
允许加载任意的Transformation和加载image通任意的目标
这个两个功能确实为该框架添加了很多色彩啊
设置转场动画,比如加载图片时,需要设置fadeIn加载的动画,看起来确实很舒服,以及暴露了可以加载自定义动画
新添加了Target
的概念,这个概念我第一次遇到确实蒙的
Target是定义了可以防止图片的目标,需要处理
//图片准备好了时该怎么展示啊
public void onImageReady(Bitmap bitmap);
//占位图片啊,要注意这里的占位图是包括加载前和加载失败的,在不同状态时,Glide会设置不同的图片
public void setPlaceholder(Drawable placeholder);
//获取到该目标能显示多大的图片,如ImageView的wrap_content时,是需要异步的等待结果的
public void getSize(SizeReadyCallback cb);
//可选性的提供转场动画
public void startAnimation(Animation animation);
//获取和设置Presenter
public void setImagePresenter(ImagePresenter imagePresenter);
public ImagePresenter getImagePresenter();
总的看,就是一个能放置图片的玩意,应该具备有什么能力(方法)
看完这个Target
,再看Presenter
吧
Presenter跟Android中的MVP中P概念是一致的,
持有View,负责捉取图片,和加载正确大的图片,即使View回收时
类型参数中的T就是Model,定义了图片的来源,这里看看设置Model的方法
1 |
|
暂且看到版本2013-8-31
感想
好累哦,感觉继续看下去自己脑子要糊了~~
2013年,有些遥远了啊,想想当时自己还没上大学呢。
考古的感觉是如何的呢?
看着大佬的一步步的从简单的版本一步步到2013-8这个版本,也快一年的工作量,消化不过来了。最初的版本的有个操作,读取文件流仅却不加载 到内存的操作,为了获取到图片源的大小,故,可以根据需要展示的图片大小,通过设置采样,不必加载整个文件,就可以加载出我们所需的图片。
这个操作在android官方文档似乎就有看到呢,且在国内的知名Android博主的文章也提及。而当自己在源码中看到这个真实的代码时,自己确实有些许 激动的。
另个一个感触是,看到开始的这些版本,感觉作者的代码,自己也是大致能完全理解的哈,Glide一步步成为知名框架,是有迹可循的。开始的代码,作者写的也 很朴素,之后代码逐渐重构清晰。结合自己看的EventBus源码,感觉大佬的炼成之路真的需要坚持啊。
另外,教训,感觉之后的源码需要切入某个方面来看,否则好难坚持。
比如作者添加错误的占位图这个功能时,根据作者写的commit,看的时候,思路就会清晰点。(小提示,以后自己提的commit也要用心点)