alighters

程序、写作、人生

图片加载理解之 UIL

| Comments

针对Android 的图片加载,有着太多的细节问题,需要注意,本文针对 Universal Image Loader 的一些技术点,进行细致的剖析一番。由于涉及的内容,所以打算分成三个系列,分别从视图(View),数据(Cache),网络等三个大的方面讲起:

视图

1. ImageLoader是如何实现对ImageView的包装的?

1)ImageAware:针对 ImageView 行为的抽象—接口,获取 ImageView 的宽度、高度、scaleType、id;以及包装的ImageView,设定图片; 2)ViewAware: 抽象类,实现ImageAware。提供了ViewRef的属性,来持有ImageView的弱引用;实现方式:this.viewRef = new WeakReference(view)。

使用弱引用的目的,避免了异步耗时任务对 ImageView 的强引用,能够使 ImageView 能够被及时回收,防止内存泄露的发生(虽然很短暂的一瞬间)。

2. 在 ListView 加载显示图片的时候,当一个正在加载图片的 View 被滑出屏幕,ImageLoader 是否会取消此次下载图片任务,是如何取消的?

首先在加载获取图片时,是通过 ImageLoaderEngine 来进行提交进行的。在 ImageLoaderEngine 是启动线程池来异步加载图片,分别从内存、磁盘、网络中进行获取。而在这几步之前,会首先进行 View 是否被回收的判断,若是被回收,则抛出异常,并调用相应 listener 的 cancel 方法。

3. 在 ListView 的滑动过程中,如何暂停所有的图片加载任务?

在 ImageLoader 中,其提供了一个 PauseOnScrollListener 的类,在使用 ListView 的时,只需进行设置即可。

其实现原理则是在通过调用 ImageLoader 的 pauseresume 方法,在调用图片加载的第一步会进行判断,是否设置了暂停状态,如果设置了,则会通过对象锁 pauseLock 的 wait 方法,来使当前图片加载线程处于阻塞状态;当调用了 resume 方法,则会调用了 pauseLock 的 notifyAll 方法,来恢复线程的执行。

这样做的作用是达到 CPU 资源的充分利用,通过暂停异步图片加载的线程,来不使 UI 线程卡顿,提高 ListView 在滑动过程的流畅程度。

4. 怎样针对 View 的特定大小,处理获取到图片的大小?

在 ImageLoader 调用 displayImage 方法时,在指定相应的 ImageView 时,也可以传递一个 ImageSize 的参数,用来指定所需显示的图片的大小;若是不传的话,则会获取 ImageView 的 width 及 height,若获取到的值为 0,那么这个相应 ImageSize 的宽与高则会取屏幕的宽与高。

另外,在对图片进行缓存时,生成相应的缓存 Key 的值是根据图片的 uritargetSize (指定的图片大小)来生成的,所以,不同大小的 ImageView 获取到的 bitmap 则是不同的,即从缓存中拿到的是不同的。

这里,可以看出相应大小的 ImageView 与内存的缓存中的不同的 bitmap 是相对应的。而 diskCache 中则是以 uri 为键值的磁盘文件。另外,由磁盘文件转换为相应的 bitmap 则是对应下面问题的答案。

5. 图片是如何进行压缩的?

我们知道图片加载到内存之中,是以 Bitmap 的形式存在的。而在 Android App 中,内存是非常稀缺的资源。所以当加载大图片时,需要根据当前显示图片的控件,采用相应手段,只在内存中加载出来相应大小的 Bitmap ,来避免 Out Of Memory 的发生。

这里采用 BitmapFactory 来进行图片文件转换至 Bitmap 对象,通过其 decodeStream 方法,若是我们传递的参数 Options,其指定了 inJustDecodeBounds 为 true, 则只会获取图片的大小(并不会生成 Bitmap 对象),其输出值为 Options 对象的 outWidthoutHeight

根据获取到的图片大小,以及我们要显示图片的 View 的大小,便可计算出我们需要对图片进行缩放的比例,即指定 Options 参数的 inSampleSize 的值。(此时 inJustDecodeBounds 的值为 false)这样获取到的 Bitmap 对象就是进行缩放调整过的图像。

这一步便是 Android 中调整 Bitmap 大小,减少内存消耗关键性的一步。

数据

数据主要体现在对图片的缓存处理。UIL 对图片的缓存为三级缓存,一是内存,二是磁盘,三是网络(远程的服务器)。

在内存中保存的为 Bitmap 对象,其是根据相应的 View 和 View 大小为 Key 值的。即加载的是同一张图片的两个不同大小的 View,会在内存缓存中存在针对这张图片的两个 Bitmap 对象。而磁盘中缓存的则只有这一张图片文件,即从远程服务器中下载到本地的图片文件。

若是本地磁盘中没有响应的图片文件的话,则会通过网络从远程服务器中下载图片至本地。

网络

从网络中获取图片,UIL 使用的是 HttpURLConnection,来执行图片的获取下载。对应逻辑代码是在 ImageLoader 接口中,其定义了由图片 imageUri,来得到 InputStream 。另外以指定 Scheme 的方式(如 HTTP,FILE,ASSETS,DRAWABLE) 来得到图片的输入流。

总结

UIL 作为图片加载的入门库,其逻辑代码也是写的非常漂亮。结构化清晰,简单明了,对各个模块都由一个接口来定义,极大地丰富细节的实现,像不同的内存、磁盘缓存策略及图片下载获取方式,另外这些策略都可以在 ImageLoader 的配置策略进行修改。总之,这个库是一个不错的学习 Android 图片加载的资料。

参考资料

版权归作者所有,转载请注明原文链接:/blog/2017/01/25/understand-imageloader/

给 Ta 个打赏吧...

Comments