ImageLoader原理分析

一般一个ImageLoader需具备以下功能:

· 图片压缩

· 内存缓存

· 磁盘缓存

· 网络拉取图片

· 图片的同步加载

· 图片的异步处理

一、对于图片压缩功能的实现:

通过采用BitmapFactory.Options来压缩图片,主要是用到了它的inSampleSize参数,当inSampleSize为1时,采样后的图片大小为原始图片大小;当inSampleSize大于1时,比如为2,那么采样后的图片的宽高均为原始图片的1/2,而像素为原图的1/4,其占有的内存大小也为原来的1/4。可以发现采样率作用于宽高,这将导致缩放后图片的大小以采样率2次方的形式递减。

那么在ImageLoader源码中是怎么确认采样率的?

1)BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片。

2)BitmapFactory.Options中取出图片的原始宽高信息,它们对应outWidth和outHeight参数。

3)根据采样率的规则并结合目标view的所需大小计算出采样率inSampleSize。

4)BitmapFactory.options的inJustDecodeBounds的参数设为false,然后重新加载图片。

 ImageLoader原理分析_第1张图片

 

二、内存缓存和磁盘缓存的实现:

采用LruCache和DiskLruCache来分别完成内存缓存和磁盘缓存的工作。在ImageLoader初始化的时候,会创建LruCache和DiskLruCache。

内存缓存和磁盘缓存的具体实现过程:

 

内存缓存:LruCache是一个泛型类,它的内部实现采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,其提供了get和put方法来完成缓存对象的获取和添加操作,当缓存对象满的时候,LruCache会移除较早的缓存对象。

 

 

磁盘缓存:DiskLruCache将缓存对象写入文件系统,从而实现缓存的效果。磁盘缓存的添加要通过Editor来完成的,Editor提供了commit和abort方法来提交和撤销对文件系统的操作。对磁盘的操作需要通过Snapshot来完成,通过Snapshot可以得到磁盘缓存对象对应的FileInputStream,但是FileInputStream无法便捷地进行压缩,所以通过FileDescriptor来加载压缩的图片,最后将加载后的Bitmap添加到内存缓存中。另外在创建磁盘缓存的时候会做一个判断,既有可能磁盘剩余空间小于磁盘缓存所需的大小,因此没有办法创建磁盘缓存,这个时候磁盘缓存就会失效。

 

 

三、同步加载和异步加载接口的设计

1)、同步加载接口设计

在源码中,loadBitmap方法体现了同步加载接口设计,它制遵循以下几个加载步骤:首先尝试从内存缓存中读取数据,接着从磁盘缓存中读取数据,最后从网络中拉取数据。另外这些步骤都是在主线程中执行的,不然会抛出异常。

2)、异步加载接口设计

在源码中,bindBitmap方法体现了异步加载接口设计。bindBitmap会尝试从内存缓存中读取图片,如果读取成功就直接返回结果,否则会在线程池中去调用loadBitmap方法,当图片加载成功以后再将图片、图片的地址以及需要绑定的imageView封装成一个LoaderResult对象,然后再通过mMainHandler向主线程发送一个消息,这样就可以在主线程中给imageView设置图片了。

 

为什么要在线程池中启动一个子线程,而没有采用普通的线程去执行任务呢?如果采用普通的线程去加载图片,随着列表的滑动着可能产生大量的线程,这样并不利于整体效率的提升。

 

 

 

附加:列表卡顿优化方案:

1、不在getView里面做耗时操作。

2、异步任务执行频率控制。如果客户频繁的上下滑动,这样会一瞬间产生上百个异步任务,这些异步任务会造成线程池的拥堵并随机产生大量的UI操作,而这些UI操作是运行在主线程中,这就会造成一定程度的卡顿(解决办法:在列表停止滑动的时候再加载图片)。

你可能感兴趣的:(Android)