图片加载理解之 UIL

AlyceHalse 8年前
   <p>针对Android 的图片加载,有着太多的细节问题,需要注意,本文针对 <a href="/misc/goto?guid=4958532889178784950" rel="nofollow,noindex">Universal Image Loader</a> 的一些技术点,进行细致的剖析一番。由于涉及的内容,所以打算分成三个系列,分别从视图(View),数据(Cache),网络等三个大的方面讲起:</p>    <h2>视图</h2>    <h3>1. ImageLoader是如何实现对ImageView的包装的?</h3>    <p>1)ImageAware:针对 ImageView 行为的抽象—接口,获取 ImageView 的宽度、高度、scaleType、id;以及包装的ImageView,设定图片; 2)ViewAware: 抽象类,实现ImageAware。提供了ViewRef的属性,来持有ImageView的弱引用;实现方式:this.viewRef = new WeakReference (view)。</p>    <p>使用弱引用的目的,避免了异步耗时任务对 ImageView 的强引用,能够使 ImageView 能够被及时回收,防止内存泄露的发生(虽然很短暂的一瞬间)。</p>    <p>2. 在 ListView 加载显示图片的时候,当一个正在加载图片的 View 被滑出屏幕,ImageLoader 是否会取消此次下载图片任务,是如何取消的?</p>    <p>首先在加载获取图片时,是通过 ImageLoaderEngine 来进行提交进行的。在 ImageLoaderEngine 是启动线程池来异步加载图片,分别从内存、磁盘、网络中进行获取。而在这几步之前,会首先进行 View 是否被回收的判断,若是被回收,则抛出异常,并调用相应 listener 的 cancel 方法。</p>    <h3>3. 在 ListView 的滑动过程中,如何暂停所有的图片加载任务?</h3>    <p>在 ImageLoader 中,其提供了一个 PauseOnScrollListener 的类,在使用 ListView 的时,只需进行设置即可。</p>    <p>其实现原理则是在通过调用 ImageLoader 的 pause 和 resume 方法,在调用图片加载的第一步会进行判断,是否设置了 <strong>暂停</strong> 状态,如果设置了,则会通过对象锁 pauseLock 的 wait 方法,来使当前图片加载线程处于阻塞状态;当调用了 resume 方法,则会调用了 pauseLock 的 notifyAll 方法,来恢复线程的执行。</p>    <p>这样做的作用是达到 CPU 资源的充分利用,通过暂停异步图片加载的线程,来不使 UI 线程卡顿,提高 ListView 在滑动过程的流畅程度。</p>    <h3>4. 怎样针对 View 的特定大小,处理获取到图片的大小?</h3>    <p>在 ImageLoader 调用 displayImage 方法时,在指定相应的 ImageView 时,也可以传递一个 ImageSize 的参数,用来指定所需显示的图片的大小;若是不传的话,则会获取 ImageView 的 width 及 height,若获取到的值为 0,那么这个相应 ImageSize 的宽与高则会取屏幕的宽与高。</p>    <p>另外,在对图片进行缓存时,生成相应的缓存 Key 的值是根据图片的 uri 和 targetSize (指定的图片大小)来生成的,所以,不同大小的 ImageView 获取到的 bitmap 则是不同的,即从缓存中拿到的是不同的。</p>    <p>这里,可以看出相应大小的 ImageView 与内存的缓存中的不同的 bitmap 是相对应的。而 diskCache 中则是以 uri 为键值的磁盘文件。另外,由磁盘文件转换为相应的 bitmap 则是对应下面问题的答案。</p>    <h3>5. 图片是如何进行压缩的?</h3>    <p>我们知道图片加载到内存之中,是以 Bitmap 的形式存在的。而在 Android App 中,内存是非常稀缺的资源。所以当加载大图片时,需要根据当前显示图片的控件,采用相应手段,只在内存中加载出来相应大小的 Bitmap ,来避免 Out Of Memory 的发生。</p>    <p>这里采用 BitmapFactory 来进行图片文件转换至 Bitmap 对象,通过其 decodeStream 方法,若是我们传递的参数 Options,其指定了 inJustDecodeBounds 为 true, 则只会获取图片的大小(并不会生成 Bitmap 对象),其输出值为 Options 对象的 outWidth 和 outHeight 。</p>    <p>根据获取到的图片大小,以及我们要显示图片的 View 的大小,便可计算出我们需要对图片进行缩放的比例,即指定 Options 参数的 inSampleSize 的值。(此时 inJustDecodeBounds 的值为 false)这样获取到的 Bitmap 对象就是进行缩放调整过的图像。</p>    <p>这一步便是 Android 中调整 Bitmap 大小,减少内存消耗关键性的一步。</p>    <h2>数据</h2>    <p>数据主要体现在对图片的缓存处理。UIL 对图片的缓存为三级缓存,一是内存,二是磁盘,三是网络(远程的服务器)。</p>    <p>在内存中保存的为 Bitmap 对象,其是根据相应的 View 和 View 大小为 Key 值的。即加载的是同一张图片的两个不同大小的 View,会在内存缓存中存在针对这张图片的两个 Bitmap 对象。而磁盘中缓存的则只有这一张图片文件,即从远程服务器中下载到本地的图片文件。</p>    <p>若是本地磁盘中没有响应的图片文件的话,则会通过网络从远程服务器中下载图片至本地。</p>    <h2>网络</h2>    <p>从网络中获取图片,UIL 使用的是 HttpURLConnection ,来执行图片的获取下载。对应逻辑代码是在 ImageLoader 接口中,其定义了由图片 imageUri,来得到 InputStream 。另外以指定 Scheme 的方式(如 HTTP,FILE,ASSETS,DRAWABLE) 来得到图片的输入流。</p>    <h2>总结</h2>    <p>UIL 作为图片加载的入门库,其逻辑代码也是写的非常漂亮。结构化清晰,简单明了,对各个模块都由一个接口来定义,极大地丰富细节的实现,像不同的内存、磁盘缓存策略及图片下载获取方式,另外这些策略都可以在 ImageLoader 的配置策略进行修改。总之,这个库是一个不错的学习 Android 图片加载的资料。</p>    <h2>参考资料</h2>    <ul>     <li><a href="/misc/goto?guid=4959735682738166139" rel="nofollow,noindex">ImageLoader Useful Info</a></li>     <li><a href="/misc/goto?guid=4958532889178784950" rel="nofollow,noindex">ImageLoader</a></li>    </ul>    <p> </p>    <p>来自:http://alighters.com/blog/2017/01/25/understand-imageloader/</p>    <p> </p>