ImageLoader的使用(Android开发艺术探索)

之前在Android开发艺术探索中讲了Bitmap的高效加载,而学到的两种缓存策略LruChache和DiskLruChache可以结合来实现成一个优秀的ImageLoader。
一个优秀的ImageLoader应该具备如下几个功能:
- 图片的同步加载
- 图片的异步加载
- 图片压缩
- 内存缓存
- 磁盘缓存
- 网络拉取
我们分步来实现,首先是图片的压缩,还是用之前学到的Bitemap.Options的inSampleSize。我们命名为ImageResizer的类来实现图片的压缩。
ImageLoader的使用(Android开发艺术探索)ImageLoader的使用(Android开发艺术探索)
接着我们选择LruChache和DiskLruChache来分别完成内存缓存和磁盘缓存。在ImageLoader初始化时,我们创建LruChache和DiskLruChache:
ImageLoader的使用(Android开发艺术探索)
其中还有对DiskLruChache的容错就是sd卡容量不足时,磁盘缓存将无效。接下来就是提供方法来添加和获取。首先是内存缓存:
ImageLoader的使用(Android开发艺术探索)
而磁盘缓存的添加在之前说过使用Editor来完成的,Editor提供了commit和abort的方法,具体参考下面的loadBimapFromHttp方法。磁盘缓存的读取是通过Snapshot来完成,通过Snapshot可以得到缓存对象对应的FileInputStream,但是FileInputStream无法用BitmapFactroty的核心解析来加载Bitmap,所以要用到描述符,最后加载Bitmap。
ImageLoader的使用(Android开发艺术探索)
ImageLoader的使用(Android开发艺术探索)

同步加载和异步加载接口的设计:
首先看同步加载,同步加载接口需要外部在线程中使用,这是因为同步加载很可能比较耗时,它的实现如下:
ImageLoader的使用(Android开发艺术探索)
在loadBitmap中,首先尝试从内存缓存中通过url来读取图片,接着尝试从磁盘中读取图片,如果都没有图片,则才从网络中去拉取图片。这个方法不能在主线程中调用,否则会抛出异常,这个环境是在loadBitmapFromHttp中实现的,通过检查当前线程的Looper是否为主线程来判断是否为主线程,如果是主线程则抛出异常。
接下来是异步加载的接口:
ImageLoader的使用(Android开发艺术探索)
异步加载中,首先现查看内存是否有Bitmap,有就直接加载,没有就在线程池中去掉用loadBitmap方法,当图片加载完毕后,就将图片,图片的地址以及要绑定的ImageView绑定成一个LoaderResult对象,然后再通过mMainHandler向主线程发送一个消息,消息的obj就是这个LoaderResult,这样就可以在主线程中的imageView设置图片了,之所以要Handler是因为子线程无法访问UI。
bindBitmap里用到了线程池和Handler,线程池通过THREAD_POOL_EXECUTOR,实现如下:
ImageLoader的使用(Android开发艺术探索)
ImageLoader的使用(Android开发艺术探索)
我们用线程池去加载图片而不是用普通的线程,是因为随着列表的滑动可能会产生大量的线程,所以线程池容易管理。接下来是Handler,ImageLoader直接采用主线程的Looper来构造Handler对象,这就使得ImageLoader可以在非主线程中构造了。另外为了解决由于view复用导致的列表错位一问题,在给ImageView设置图片之前要查看它的url有没有发生改变,如果改变了就不设置了。
ImageLoader的使用(Android开发艺术探索)
ImageLoader的使用(Android开发艺术探索)
到此为止ImageLoader的基本代码就完成了。所有的代码在《Android开发艺术探索》p433-441
接下来的实战就是用之前的ImageLoader做一个照片墙,照片墙需要GridView,在给item的image一个自定义成为SquareImageView,就是让它继承ImageView,然后重写onMeasure中,将super构造方法的两个参数的长和宽都设置成一样就是了,接下来就是GridView的Adapter:
ImageLoader的使用(Android开发艺术探索)
ImageLoader的使用(Android开发艺术探索)
这个adapter非常典型,可以学习学习,最后在Activity配置它,就完成了照片墙的功能了。

优化列表的卡顿现象
首先不要再getView进行耗时操作,因为加载图片是耗时任务,这种操作必须通过异步的方式来处理。上述的getView就是将加载图片交给了ImageLoader。
其次是控制异步加载的频率。可以在列表停止滑动的时候停止加载图片。