史上最全Universal-Image-Loader源码解析————缓存篇

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a820703048/article/details/79154332

背景

在我写下这篇博客的时候,我还是一名二本学校计算机专业大四的应届毕业生,自学Android开发快两年了,这两年的时间里面,其实真的是感慨万千,两年的时间里面,Android的很多事情也都见识过了,其实Android对于新手入门在就业方面是相当不友好的事情。都说第一个吃螃蟹的人最好,是的,Android从2012年那段时间火起来,那时候Android开发会个ListView都被大厂强着要,时过境迁,现在随着大批中小创业公司的倒台,微信一个App的小程序都垄断绝大部分应用,再加上在大前端 WebApp的出现,混合开发,跨平台等开发的出现,现在很多初级中级Android程序员真没有就业出路,一个岗位企业面对成百上千的开发者应聘,当然也就挑挑拣拣,面试的题目也算是越来越难,从Java基础到多线程,到开源框架的源码设计,等等都会考察到,没办法上了这条贼船,也只能坐下去了,下面先带大家了解第一个开源框架ImageLoader 相信这个开源框架应该是最常用也是用到最多的开源框架了。

一、源码框架

关于这个开源框架的项目架构我直接上图更加清晰,可以看到分为内存相关,工具类相关,和核心代码相关

这里写图片描述

二、内存缓存和磁盘缓存的相关

可以看到分为内存缓存和磁盘缓存

这里写图片描述

三、内存缓存相关的类与操作

下面是内存缓存的每一个类所代表的意思和作用,我们随后将逐个进行每个类每个方法的讲解。

这里写图片描述

(1) MemoryCahce 接口

接口中定义了正删改查等方法,详情看图

这里写图片描述

(2)baseMemoryCache实现接口的一个基类

这个基类定义了 一个弱引用还有就是实现增删改查的方法

这里写图片描述

(3)LimitedMemoryCache

这个类继承了BaseMemoryCache这个类 也就是说也是使用弱引用,限制最大内存为16M,重点在于,添加缓存的逻辑,详细看代码 详情看注解

public abstract class LimitedMemoryCache extends BaseMemoryCache {
    private static final int MAX_NORMAL_CACHE_SIZE_IN_MB = 16;
    private static final int MAX_NORMAL_CACHE_SIZE = MAX_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024; //限制图片大小为16M最大
    private final int sizeLimit; //限制数
    private final AtomicInteger cacheSize;
    /**
     * Contains strong references to stored objects. Each next object is added last. If hard cache size will exceed
     * limit then first object is deleted (but it continue exist at {@link #softMap} and can be collected by GC at any
     * time)
     */
    private final List<Bitmap> hardCache = Collections.synchronizedList(new LinkedList<Bitmap>());
    /** @param sizeLimit Maximum size for cache (in bytes) */
    public LimitedMemoryCache(int sizeLimit) {
        this.sizeLimit = sizeLimit;
        cacheSize = new AtomicInteger();
        if (sizeLimit > MAX_NORMAL_CACHE_SIZE) {
        }
    }
   //添加图片到缓存
    @Override
    public boolean put(String key, Bitmap value) {
        boolean putSuccessfully = false;
        // Try to add value to hard cache
        int valueSize = getSize(value);//先拿到要放进去的图片的大小
        int sizeLimit = getSizeLimit();//拿到大小的限制
        int curCacheSize = cacheSize.get();//拿到目前多大
        if (valueSize < sizeLimit) {
            while (curCacheSize + valueSize > sizeLimit) {//做循环直到内存够为止
                Bitmap removedValue = removeNext();//取出要删除的哪一个
                if (hardCache.remove(removedValue)) {//删除掉
                    curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
                }
            }
            hardCache.add(value);//加入图片作为缓存
            cacheSize.addAndGet(valueSize);//添加缓存
            putSuccessfully = true;
        }
        // Add value to soft cache
        super.put(key, value);
        return putSuccessfully;
    }
 //删除图片从缓存
    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            if (hardCache.remove(value)) {
                cacheSize.addAndGet(-getSize(value));
            }
        }
        return super.remove(key);
    }
 //清空
    @Override
    public void clear() {
        hardCache.clear();
        cacheSize.set(0);
        super.clear();
    }
      //
    protected int getSizeLimit() {
        return sizeLimit;
    }
    protected abstract int getSize(Bitmap value);
    protected abstract Bitmap removeNext();
}

(4)WeakMemoryCache 只使用弱引用 因为他重写了父类BaseMemoryCache 的createReference 方法 而父类在放入缓存的时候会调用这个方法这样就把强引用弄成弱引用,在内存不够的时候自动被回收,各个方法的详情看注解

public class WeakMemoryCache extends BaseMemoryCache {
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

(5)UsingFreqLimitedMemoryCache

LimitedMemoryCache这个是一个最近使用最少的缓存策略用一个Map

public class UsingFreqLimitedMemoryCache extends LimitedMemoryCache {
    private final Map<Bitmap, Integer> usingCounts = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());
    public UsingFreqLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }
    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {//调用父类的放入缓存方法
            usingCounts.put(value, 0);//记录这个为0
            return true;
        } else {
            return false;
        }
    }
    @Override
    public Bitmap get(String key) {
        Bitmap value = super.get(key);
        // Increment usage count for value if value is contained in hardCahe
        if (value != null) {//拿出如果有对象,那么把这个对象使用次数加1
            Integer usageCount = usingCounts.get(value);
            if (usageCount != null) {
                usingCounts.put(value, usageCount + 1);
            }
        }
        return value;
    }
    @Override
    public Bitmap remove(String key) {//删除就删除掉,使用记录也随之删除掉了
        Bitmap value = super.get(key);
        if (value != null) {
            usingCounts.remove(value);
        }
        return super.remove(key);
    }
    @Override
    public void clear() { //清空就将记录清空 调用父类清空
        usingCounts.clear();
        super.clear();
    }
    @Override
    protected int getSize(Bitmap value) {//拿到图片的大小
        return value.getRowBytes() * value.getHeight();
    }
    @Override
    protected Bitmap removeNext() {//获取删除下一个 这个是遍历记录集合把使用最少的标记出来,在父类删除掉,这个记录也将删除掉
        Integer minUsageCount = null;
        Bitmap leastUsedValue = null;
        Set<Entry<Bitmap, Integer>> entries = usingCounts.entrySet();
        synchronized (usingCounts) {
            for (Entry<Bitmap, Integer> entry : entries) {
                if (leastUsedValue == null) {
                    leastUsedValue = entry.getKey();
                    minUsageCount = entry.getValue();
                } else {
                    Integer lastValueUsage = entry.getValue();
                    if (lastValueUsage < minUsageCount) {
                        minUsageCount = lastValueUsage;
                        leastUsedValue = entry.getKey();
                    }
                }
            }
        }
        usingCounts.remove(leastUsedValue);
        return leastUsedValue;
    }
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {//强行转化为弱引用
        return new WeakReference<Bitmap>(value);
    }
}

(6)LruMemoryCache最近最少使用内存策略

也是默认的缓存策略,在下一章可能会提到配置参数的时候,其实他就说实现了一个MemoryCache接口 实现增删改查的方法,初始化了一个强引用 LinkedHashMap 这个数据结构的特点就说会记录最近使用的频率具体的方法实现看代码注解

public class LruMemoryCache implements MemoryCache {
    private final LinkedHashMap<String, Bitmap> map;
    private final int maxSize;
    /** Size of this cache in bytes */
    private int size;
    /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
    public LruMemoryCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
    }
    /**
     根据键值直接从map中拿到对应大小
     */
    @Override
    public final Bitmap get(String key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }
        synchronized (this) {
            return map.get(key);
        }
    }
    //添加的方法
    @Override
    public final boolean put(String key, Bitmap value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }
        synchronized (this) {
            size += sizeOf(key, value);//调用sizeOf这个函数获得该bitmap对象的占用内存的大小,并且让缓存总数增加
            Bitmap previous = map.put(key, value);//这里就是把对象放入容器中的最核心的一句代码,如果容器中已经有了此元素,则返回该元素的value值,否则返回空
            if (previous != null) {
                size -= sizeOf(key, previous);//如果容器中已经有了此元素,则需要把增加的数量减掉
            }
        }
        trimToSize(maxSize);
        return true;
    }
    /**此函数计算是否超出最大限量,是则删除队尾元素
     */
    private void trimToSize(int maxSize) {
        while (true) {
            String key;
            Bitmap value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
                }
                if (size <= maxSize || map.isEmpty()) {
                    break;
                }
                Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();//拿到队尾的一个删除掉
                if (toEvict == null) {
                    break;
                }
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= sizeOf(key, value);
            }
        }
    }
      //删除的方法 使用同步代码块 删除从map 当然size要减掉相应大小
    @Override
    public final Bitmap remove(String key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }
        synchronized (this) {
            Bitmap previous = map.remove(key);
            if (previous != null) {
                size -= sizeOf(key, previous);
            }
            return previous;
        }
    }
//拿到所有的键
    @Override
    public Collection<String> keys() {
        synchronized (this) {
            return new HashSet<String>(map.keySet());
        }
    }
      //*=清楚的方法传一个负一
    @Override
    public void clear() {
        trimToSize(-1); // -1 will evict 0-sized elements
    }

    /**算出大小
     */
    private int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
    @Override
    public synchronized final String toString() {
        return String.format("LruCache[maxSize=%d]", maxSize);
    }
}

(7)LRULimitedMemoryCache这个和刚刚LruMemoryCache,基本相似只不过,它继承的是LimitedMemoryCache,使用的是弱引用,这里就不多说了直接看代码详细方法细节看注解

public class LRULimitedMemoryCache extends LimitedMemoryCache {
    private static final int INITIAL_CAPACITY = 10;
    private static final float LOAD_FACTOR = 1.1f;
    /** Cache providing Least-Recently-Used logic */
    private final Map<String, Bitmap> lruCache = Collections.synchronizedMap(new LinkedHashMap<String, Bitmap>(INITIAL_CAPACITY, LOAD_FACTOR, true));
    /** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
    public LRULimitedMemoryCache(int maxSize) {//设置最大值
        super(maxSize);
    }
    @Override
    public boolean put(String key, Bitmap value) {//添加的方法重写
        if (super.put(key, value)) {
            lruCache.put(key, value);
            return true;
        } else {
            return false;
        }
    }
    @Override
    public Bitmap get(String key) {//重写直接从父类中拿
        lruCache.get(key); // call "get" for LRU logic
        return super.get(key);
    }
    @Override
    public Bitmap remove(String key) {//删除 直接从父类删除当然本地的也要删除
        lruCache.remove(key);
        return super.remove(key);
    }
    @Override
    public void clear() {
        lruCache.clear();
        super.clear();
    }
    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
    @Override
    protected Bitmap removeNext() {//拿到LinkHashMap值最后一个也就是使用次数最少的哪一个给父类去删除
        Bitmap mostLongUsedValue = null;
        synchronized (lruCache) {
            Iterator<Entry<String, Bitmap>> it = lruCache.entrySet().iterator();
            if (it.hasNext()) {
                Entry<String, Bitmap> entry = it.next();
                mostLongUsedValue = entry.getValue();
                it.remove();
            }
        }
        return mostLongUsedValue;
    }

    /**
     * 重写导致弱引用
     * @param value
     * @return
     */
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

(8)LimitedAgeMemoryCache

传入一个接口和一个生命值,把时间到的缓存给删除掉,详细看代码方法注解

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package leakcanary.imageloader.cache.memory.impl;

import android.graphics.Bitmap;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import leakcanary.imageloader.cache.memory.MemoryCache;

/**当 bitmap加入缓存中的时间超过我们设定的值,将其删除
 */
public class LimitedAgeMemoryCache implements MemoryCache {
    private final MemoryCache cache;//接口 可以看成BaseMemoryCache这样的类
    private final long maxAge;//最大生命值
    private final Map<String, Long> loadingDates = Collections.synchronizedMap(new HashMap<String, Long>());
   //一个是键值一个生命时间
    /**初始化
     */
    public LimitedAgeMemoryCache(MemoryCache cache, long maxAge) {
        this.cache = cache;
        this.maxAge = maxAge * 1000; // to milliseconds
    }

    /**
     放入调用接口的保存方法 ,然后记录最新的方法
     */
    @Override
    public boolean put(String key, Bitmap value) {
        boolean putSuccesfully = cache.put(key, value);
        if (putSuccesfully) {
            loadingDates.put(key, System.currentTimeMillis());
        }
        return putSuccesfully;
    }
//根据键值拿到 如果超过了有效期就删除掉
    @Override
    public Bitmap get(String key) {
        Long loadingDate = loadingDates.get(key);
        if (loadingDate != null && System.currentTimeMillis() - loadingDate > maxAge) {
            cache.remove(key);
            loadingDates.remove(key);
        }
        return cache.get(key);
    }
     //删除掉
    @Override
    public Bitmap remove(String key) {
        loadingDates.remove(key);
        return cache.remove(key);
    }

    @Override
    public Collection<String> keys() {
        return cache.keys();
    }
//清除掉
    @Override
    public void clear() {
        cache.clear();
        loadingDates.clear();
    }
}

(9)LargestLimitedMemoryCache

LimitedMemoryCache继承LimitedMemoryCache,其实就是拿一个集合 Map

public class LargestLimitedMemoryCache extends LimitedMemoryCache {
    //map图片大小和bitmap
    private final Map<Bitmap, Integer> valueSizes = Collections.synchronizedMap(new HashMap<Bitmap, Integer>());

    public LargestLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }

    //直接放进去key是 计算大小
    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {
            valueSizes.put(value, getSize(value));
            return true;
        } else {
            return false;
        }
    }
    //删除就记录大小的集合也删除
    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            valueSizes.remove(value);
        }
        return super.remove(key);
    }
 //清除
    @Override
    public void clear() {
        valueSizes.clear();
        super.clear();
    }
//拿到大小
    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
//拿到最大的那个返回回去父类删除掉
    @Override
    protected Bitmap removeNext() {
        Integer maxSize = null;
        Bitmap largestValue = null;
        Set<Entry<Bitmap, Integer>> entries = valueSizes.entrySet();
        synchronized (valueSizes) {
            for (Entry<Bitmap, Integer> entry : entries) {
                if (largestValue == null) {
                    largestValue = entry.getKey();
                    maxSize = entry.getValue();
                } else {
                    Integer size = entry.getValue();
                    if (size > maxSize) {
                        maxSize = size;
                        largestValue = entry.getKey();
                    }
                }
            }
        }
        valueSizes.remove(largestValue);
        return largestValue;
    }
//使用弱引用
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

(10)FIFOLimitedMemoryCache先进先出缓存策略

其实就维护了一个List queue

/*******************************************************************************
 * Copyright 2011-2014 Sergey Tarasevich
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package leakcanary.imageloader.cache.memory.impl;

import android.graphics.Bitmap;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import leakcanary.imageloader.cache.memory.LimitedMemoryCache;

/**
 * 先进先出缓存策略
 * Limited {@link Bitmap bitmap} cache. Provides {@link Bitmap bitmaps} storing. Size of all stored bitmaps will not to
 * exceed size limit. When cache reaches limit size then cache clearing is processed by FIFO principle.<br />
 * <br />
 * <b>NOTE:</b> This cache uses strong and weak references for stored Bitmaps. Strong references - for limited count of
 * Bitmaps (depends on cache size), weak references - for all other cached Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.0.0
 */
public class FIFOLimitedMemoryCache extends LimitedMemoryCache {
    //构建一个队列 用一个线性列表
    private final List<Bitmap> queue = Collections.synchronizedList(new LinkedList<Bitmap>());

    public FIFOLimitedMemoryCache(int sizeLimit) {
        super(sizeLimit);
    }

    //增加直接往这个队列中增加
    @Override
    public boolean put(String key, Bitmap value) {
        if (super.put(key, value)) {
            queue.add(value);
            return true;
        } else {
            return false;
        }
    }
     //从队列中删除
    @Override
    public Bitmap remove(String key) {
        Bitmap value = super.get(key);
        if (value != null) {
            queue.remove(value);
        }
        return super.remove(key);
    }
  //清空
    @Override
    public void clear() {
        queue.clear();
        super.clear();
    }
     //拿到一个图片的大小
    @Override
    protected int getSize(Bitmap value) {
        return value.getRowBytes() * value.getHeight();
    }
   //删除队列头
    @Override
    protected Bitmap removeNext() {
        return queue.remove(0);
    }
//使用弱引用
    @Override
    protected Reference<Bitmap> createReference(Bitmap value) {
        return new WeakReference<Bitmap>(value);
    }
}

好了所有内存缓存策略的每个方法和实现原理都说了现在看磁盘缓存了

三、磁盘缓存的操作与架构

我们先来看一下磁盘缓存的每个类的架构,后面将针对每个类来进行分析

这里写图片描述

(1)DiskCache 一个接口定义了文件的各种操作 详细看代码注解

public interface DiskCache {
    /**
    拿到磁盘的目录路劲
     */
    File getDirectory();
    /**
       根据uri得到文件
     */
    File get(String imageUri);
    /**
    保存图片和url
     */
    boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException;
    /**
        保存图片
     */
    boolean save(String imageUri, Bitmap bitmap) throws IOException;
    /**
     根据uri删除.
     */
    boolean remove(String imageUri);
    /** 关闭资源 */
    void close();
    /** 清空*/
    void clear();
}

(2)FileNameGenerator

根据Uri生成唯一文件命名的接口,下面分别是使用HashCode实现和MD5实现

public interface FileNameGenerator {

    /** 生成由URI定义的图像的唯一文件名。 */
    String generate(String imageUri);
}
/**
 使用hashCode的方式生成唯一文件名
 */
public class HashCodeFileNameGenerator implements FileNameGenerator {
    @Override
    public String generate(String imageUri) {
        return String.valueOf(imageUri.hashCode());
    }
}
/**
 *根据uri 根据MD5的方式生成为唯一文件名
 */
public class Md5FileNameGenerator implements FileNameGenerator {
    private static final String HASH_ALGORITHM = "MD5";
    private static final int RADIX = 10 + 26; // 10 digits + 26 letters
    @Override
    public String generate(String imageUri) {
        byte[] md5 = getMD5(imageUri.getBytes());
        BigInteger bi = new BigInteger(md5).abs();
        return bi.toString(RADIX);
    }
    private byte[] getMD5(byte[] data) {
        byte[] hash = null;
        try {
            MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
            digest.update(data);
            hash = digest.digest();
        } catch (NoSuchAlgorithmException e) {
        }
        return hash;
    }
}

(2)BaseDiskCache 磁盘缓存类的一个比较重要的方法 下面两个子类基本是实现父类的方法


public abstract class BaseDiskCache implements DiskCache {
//BUFFER大小
    public static final int DEFAULT_BUFFER_SIZE = 32 * 1024; // 32 Kb
    /** {@value */
    public static final Bitmap.CompressFormat DEFAULT_COMPRESS_FORMAT = Bitmap.CompressFormat.PNG;
    /** {@value */
    public static final int DEFAULT_COMPRESS_QUALITY = 100;

    private static final String ERROR_ARG_NULL = " argument must be not null";
    private static final String TEMP_IMAGE_POSTFIX = ".tmp";//临时文件的扩展名

    protected final File cacheDir;////缓存地址
    protected final File reserveCacheDir;//cache//备用地址

    protected final FileNameGenerator fileNameGenerator;//文件名生成器

    protected int bufferSize = DEFAULT_BUFFER_SIZE;

    protected Bitmap.CompressFormat compressFormat = DEFAULT_COMPRESS_FORMAT;
    protected int compressQuality = DEFAULT_COMPRESS_QUALITY;

    /** 一个参数 传地址*/
    public BaseDiskCache(File cacheDir) {
        this(cacheDir, null);
    }

    /**文件地址 和备用地址 如果为空就传用HashCode生成命名
     */
    public BaseDiskCache(File cacheDir, File reserveCacheDir) {
        this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator());
    }

    /**
可以传递三个参数的
     */
    public BaseDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
        if (cacheDir == null) {
            throw new IllegalArgumentException("cacheDir" + ERROR_ARG_NULL);
        }
        if (fileNameGenerator == null) {
            throw new IllegalArgumentException("fileNameGenerator" + ERROR_ARG_NULL);
        }
        this.cacheDir = cacheDir;
        this.reserveCacheDir = reserveCacheDir;
        this.fileNameGenerator = fileNameGenerator;
    }
     //拿到路劲
    @Override
    public File getDirectory() {
        return cacheDir;
    }
    //根据uri拿到文件
    @Override
    public File get(String imageUri) {
        return getFile(imageUri);
    }
//将流中数据存储到文件中,先把内容写到临时文件中,然后再重命名为目标文件
    @Override
    public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
        File imageFile = getFile(imageUri);
        File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
        boolean loaded = false;
        try {
            OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
            try {
                loaded = IoUtils.copyStream(imageStream, os, listener, bufferSize);
            } finally {
                IoUtils.closeSilently(os);
            }
        } finally {
            if (loaded && !tmpFile.renameTo(imageFile)) {
                loaded = false;
            }
            if (!loaded) {
                tmpFile.delete();
            }
        }
        return loaded;
    }
    //保存图片,也是采用先写到临时文件在重命名的策略,这个可以借鉴
    @Override
    public boolean save(String imageUri, Bitmap bitmap) throws IOException {
        File imageFile = getFile(imageUri);
        File tmpFile = new File(imageFile.getAbsolutePath() + TEMP_IMAGE_POSTFIX);
        OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile), bufferSize);
        boolean savedSuccessfully = false;
        try {
            savedSuccessfully = bitmap.compress(compressFormat, compressQuality, os);
        } finally {
            IoUtils.closeSilently(os);
            if (savedSuccessfully && !tmpFile.renameTo(imageFile)) {
                savedSuccessfully = false;
            }
            if (!savedSuccessfully) {
                tmpFile.delete();
            }
        }
        bitmap.recycle();
        return savedSuccessfully;
    }
     //删除指定文件
    @Override
    public boolean remove(String imageUri) {
        return getFile(imageUri).delete();
    }

    @Override
    public void close() {
        // Nothing to do
    }
//清空文件夹
    @Override
    public void clear() {
        File[] files = cacheDir.listFiles();
        if (files != null) {
            for (File f : files) {
                f.delete();
            }
        }
    }
    /**
     * 将传过来的uri用文件名生成器搞一下拿到这个图片地址所在的资源
     * */
    protected File getFile(String imageUri) {
        String fileName = fileNameGenerator.generate(imageUri);
        File dir = cacheDir;
        if (!cacheDir.exists() && !cacheDir.mkdirs()) {
            if (reserveCacheDir != null && (reserveCacheDir.exists() || reserveCacheDir.mkdirs())) {
                dir = reserveCacheDir;
            }
        }
        return new File(dir, fileName);
    }
//返回BufferSize
    public void setBufferSize(int bufferSize) {
        this.bufferSize = bufferSize;
    }

    public void setCompressFormat(Bitmap.CompressFormat compressFormat) {
        this.compressFormat = compressFormat;
    }
    public void setCompressQuality(int compressQuality) {
        this.compressQuality = compressQuality;
    }
}

(4)UnlimitedDiskCache

没有限制的磁盘缓存方法,基本上是调用父类的方法就好了

public class UnlimitedDiskCache extends BaseDiskCache {
    public UnlimitedDiskCache(File cacheDir) {
        super(cacheDir);
    }
    public UnlimitedDiskCache(File cacheDir, File reserveCacheDir) {
        super(cacheDir, reserveCacheDir);
    }
    public UnlimitedDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator) {
        super(cacheDir, reserveCacheDir, fileNameGenerator);
    }
}

(5)LimitedAgeDiskCache有限制的磁盘缓存策略 需要传多一个生命值维护一个 Map

public class LimitedAgeDiskCache extends BaseDiskCache {
     //最大生命值
    private final long maxFileAge;
    //一个集合来规定每个文件的生命时长
    private final Map<File, Long> loadingDates = Collections.synchronizedMap(new HashMap<File, Long>());
    public LimitedAgeDiskCache(File cacheDir, long maxAge) {
        this(cacheDir, null, DefaultConfigurationFactory.createFileNameGenerator(), maxAge);
    }
    public LimitedAgeDiskCache(File cacheDir, File reserveCacheDir, long maxAge) {
        this(cacheDir, reserveCacheDir, DefaultConfigurationFactory.createFileNameGenerator(), maxAge);
    }
    public LimitedAgeDiskCache(File cacheDir, File reserveCacheDir, FileNameGenerator fileNameGenerator, long maxAge) {
        super(cacheDir, reserveCacheDir, fileNameGenerator);
        this.maxFileAge = maxAge * 1000; // to milliseconds
    }
  //获取文件 只是在获取的时候会判断是否应该将其删除
    @Override
    public File get(String imageUri) {
        File file = super.get(imageUri);
        if (file != null && file.exists()) {
            boolean cached;
            Long loadingDate = loadingDates.get(file);
            if (loadingDate == null) {
                cached = false;
                loadingDate = file.lastModified();//拿到最后一次修改的时间
            } else {
                cached = true;
            }
            if (System.currentTimeMillis() - loadingDate > maxFileAge) {//如果最后一次修改的时间和现在的时间差大于生命周期那么就要删除掉
                file.delete();
                loadingDates.remove(file);
            } else if (!cached) {
                loadingDates.put(file, loadingDate);
            }
        }
        return file;
    }
     //放进去的时候要刷新时间
    @Override
    public boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener) throws IOException {
        boolean saved = super.save(imageUri, imageStream, listener);
        rememberUsage(imageUri);
        return saved;
    }
    //放进去的时候要刷新时间
    @Override
    public boolean save(String imageUri, Bitmap bitmap) throws IOException {
        boolean saved = super.save(imageUri, bitmap);
        rememberUsage(imageUri);
        return saved;
    }
    //删除就直接删除掉
    @Override
    public boolean remove(String imageUri) {
        loadingDates.remove(getFile(imageUri));
        return super.remove(imageUri);
    }
       //清空
    @Override
    public void clear() {
        super.clear();
        loadingDates.clear();
    }
//刷新使用时间
    private void rememberUsage(String imageUri) {
        File file = getFile(imageUri);
        long currentTime = System.currentTimeMillis();
        file.setLastModified(currentTime);
        loadingDates.put(file, currentTime);
    }
}

四、总结

(1)ImageLoad的缓存类大概就就介绍这些,其实缓存的原理主要是要理解实现的过程,对于使用的步骤可以结合这个图去记忆。

这里写图片描述

① UI:请求数据,使用唯一的Key值索引Memory Cache中的Bitmap。

② 内存缓存:缓存搜索,如果能找到Key值对应的Bitmap,则返回数据。否则执行第三步。

③ 硬盘存储:使用唯一Key值对应的文件名,检索SDCard上的文件。

④ 如果有对应文件,使用BitmapFactory.decode*方法,解码Bitmap并返回数据,同时将数据写入缓存。如果没有对应文件,执行第五步。

⑤ 下载图片:启动异步线程,从数据源下载数据(Web)。

⑥ 若下载成功,将数据同时写入硬盘和缓存,并将Bitmap显示在UI中

(2)内存缓存默认LruMemoryCache ,初始化代码可以如下传入你需要的缓存配置

ImageLoaderConfiguration config = new
ImageLoaderConfiguration .Builder(getApplicationContext())
.maxImageWidthForMemoryCache(800)
.maxImageHeightForMemoryCache(480)
.httpConnectTimeout(5000)
.httpReadTimeout(20000)
.threadPoolSize(5)
.threadPriority(Thread.MIN_PRIORITY + 3)
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new UsingFreqLimitedCache(2000000)) // 你可以传入自己的内存缓存
.discCache(new UnlimitedDiscCache(cacheDir)) // 你可以传入自己的磁盘缓存
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.build();

猜你喜欢

转载自blog.csdn.net/a820703048/article/details/79154332