开源框架KImageLoader开发及原理剖析(二)

开源框架KImageLoader的实现原理与大部分的图片加载器一样,内部采用线程池+二级缓存架构,并且尽量做到可配置,可替换。

KImageLoader开源框架Github地址:https://github.com/dolpphins/KImageLoader


KImageLoader整体架构图

开源框架KImageLoader开发及原理剖析(二)_第1张图片


KImageLoader类图

开源框架KImageLoader开发及原理剖析(二)_第2张图片


KImageLoader关键类

ImageLoader: ImageLoader采用了单例模式,也就是应用中有且仅有一个实例。为了使程序结构更加清晰和提高可扩展性,ImageLoader只是起到转发的作用,在ImageLoader中并没有实现过多的业务处理逻辑。它只是把提交过来的任务打包成一个ImageLoadTask对象,然后转发给ImageLoaderAssistant。而且如果发现调用的是接收一个ImageView的displayImage方法,它会先将一个ImageView对象转化为一个displayImage列表,这使得ImageLoader的一对一、一对多的加载模式可以统一起来。

ImageLoaderAssistant: 协助ImageLoader处理相关的逻辑,比如回调监听接口,提交任务给图片加载执行器。

ImageLoaderExecutor: 图片加载执行器,内部维护了一个用于图片加载的线程池,该线程池采用PriorityBlockingQueue作为线程队列,因此客户端代码可以指定其任务的优先级以调整任务的执行优先次序,队列长度为128,进队列时如果发现队列已满则等待知道队列不为满为止。由于在线程执行完需要进行相关操作(设置图片等),所以execute的是一个Future而不是一个Runnable,而且需要把Future存起来,以便待会可以通过它拿到线程执行后得到结果,而且也可以防止任务的重复提交。

ImageLoadActualizer: 任务真正的执行者,它会根据提交的任务的选项采取相关的操作,比如设置允许图片缓存在内存中,那么它在获取到图片后就会将图片缓存到内存中,磁盘缓存也一样。而具体的内存缓存算法,磁盘缓存算法,图片下载处理都是配置的对应的管理器实现的,ImageLoadActualizer类并不关心。KImageLoader为内存缓存、磁盘缓存和下载器提供了默认的实现类,用户代码也可以自行指定特定的管理器。

ICache: ICache是所有缓存的公共接口,它提供了缓存的相关公共操作接口方法,比如典型的put和get方法。而DiskCache和MemoryCache两个接口都继承自ICache接口,分别代表内存缓存和磁盘缓存,它们各自提供了相应的相关接口方法。

Downloader: 下载接口,所有下载器都必须实现该接口。


KImageLoader缓存类

上面说到KImageLoader提供了默认的内存缓存和磁盘缓存实现类,它们分别采用Android中的LruCache类和开源项目DiskLruCache实现。

内存缓存: 在KImageLoader中对应的就是LruMemoryCache类,从名字就可以看出是采用了LRU算法进行缓存管理的,该类使用v4包下的LruCache实现,注意这个与android.util包下的LruCache类是有区别的,可以查看情景学习Android中的LruCache这篇文章。LruMemoryCache类中指定内存缓存的最大大小为虚拟机堆的最大大小的1/6:

    /** 最大缓存大小,单位:字节,默认为堆最大大小的1/6*/
    private final static int MAX_CACHE_SIZE = (int) (Runtime.getRuntime().maxMemory() / 6);

    private static android.support.v4.util.LruCache<String, Bitmap> sBitmapCache = new android.support.v4.util.LruCache<String, Bitmap>(MAX_CACHE_SIZE){

        @Override
        protected int sizeOf(String key, Bitmap value) {
            return BitmapUtils.sizeOfBitmap(value);
        }
    };

为了防止OOM,在put的时候会检查可用内存大小,内存大于一定的值才会将图片放入缓存中:

    @Override
    public boolean put(String key, Bitmap value) {
        if(TextUtils.isEmpty(key) || value == null) {
            return false;
        } else {
            //判断是否有足够的内存
            if(checkRemainderMemory(value)) {
                sBitmapCache.put(key, value);
                return true;
            } else {
                return false;
            }
        }
    }

由于sBitmapCache被声明为static的,这使得它的生命周期跟应用一样长,在缓存不使用时为了避免占用内存,应该及时将其移除掉,LruMemoryCache提供了clear方法用于移除所有的缓存。

磁盘缓存: 对应KImageLoader中的BitmapDiskLruCache类,采用开源项目DiskLruCache实现LRU算法的磁盘缓存。


相关优化细节

1.从网络上下载图片时,并不是直接下载到内存,而是采取复制流数据到磁盘中的方法进行下载,因为如果要下载的图片很大,直接下载到内存中可能会导致OOM。下载到磁盘上然后再按一定的质量decode一张Bitmap,可以有效地防止OOM的发生。

2.每次提交任务时都会检查是否已经存在相同的任务了,如果时就直接返回。这个在AbsListView的使用过程中会经常出现(一般都是getView被调用多次造成的),至于如果判断两个任务是否是相同的,可以查看ImageLoadTask类重写的equals方法和hashCode方法。

3.上面提到,为了避免OOM,在添加到内存缓存时,会进行可用内存计算,如果这时内存足够才会把bitmap缓存到内存中,也就是说即使设置了内存缓存,也不一定100%会被缓存到内存中。

4.使用OkHttp进行网络下载图片,OkHttp具有连接复用,自动重试,gzip压缩等优点。

转载请注明原文地址:http://blog.csdn.net/u012619640/article/details/50554996

你可能感兴趣的:(android,缓存,开源框架,图片加载器)