当前位置: 代码迷 >> Android >> Android大图加载优化-基于LRU算法的本地文件缓存
  详细解决方案

Android大图加载优化-基于LRU算法的本地文件缓存

热度:27   发布时间:2016-04-27 23:18:36.0
Android大图加载优化--基于LRU算法的本地文件缓存

前言

在博客:Android大图加载内存优化(如何防止OutOfMemmory)中讲解了在加载图片的时候内存不完全加载原图或预估图片的大小,加载合适的尺寸的图片防止OOM。接下来讲解图片文件的本地缓存,网络图片必须经过本地缓存,才能提高资源的访问速度,内存的缓存必须配合SDCard的缓存,才能发挥它的优势。本文采用的是LRU本地缓存策略,由于本文侧重的是文件的缓存,所以没有引入内存的缓存,也没有发挥出前一篇博客降到的图片加载优势,不过在后续的博客中我将不断完善整个项目,带领大家一起揭秘第三方图片加载库。

LRU算法

在图片的加载中,还有一个重要的步骤,是网络图片的本地缓存,很多时候不知道缓存的图片不知道何时删除,这时候需要一个合理的本地图片缓存策略,保证图片文件不会无限制的占用存储空间导致存储空间不足,造成资源的浪费。在计算机操作系统里边对任务的调度引入了LRU算法 。通俗的讲就是把就是把最长时间内未使用的资源优先级放到最低,优先保证使用频率高的资源。

图片本地缓存核心代码

增加一个配置类,可以根据这个类配置图片文件的缓存位置,大小等参数,后续可能需要的配置都要在这个里边扩展。
/** * Created by CJstar on 15/8/24. */public final class FileCacheOptions {    /**     * the file cache root path     */    private String cacheRootPath;    /**     * file cache count     */    private int maxFileCount;    /**     * file cache max size: byte     */    private int maxCacheSize;    /**     * if it is false, will not cache files     */    private boolean isUseFileCache = true;    public String getCacheRootPath() {        return cacheRootPath;    }    public void setCacheRootPath(String cacheRootPath) {        this.cacheRootPath = cacheRootPath;    }    public int getMaxFileCount() {        return maxFileCount;    }    public void setMaxFileCount(int maxFileCount) {        this.maxFileCount = maxFileCount;    }    /**     * cache size in bytes     * @return     */    public int getMaxCacheSize() {        return maxCacheSize;    }    public void setMaxCacheSize(int maxCacheSize) {        this.maxCacheSize = maxCacheSize;    }    public boolean isUseFileCache() {        return isUseFileCache;    }    public void setIsUseFileCache(boolean isUseFileCache) {        this.isUseFileCache = isUseFileCache;    }    private FileCacheOptions(Builder builder){        setCacheRootPath(builder.getCacheRootPath());        setIsUseFileCache(builder.isUseFileCache());        setMaxCacheSize(builder.getMaxCacheSize());        setMaxFileCount(builder.getMaxFileCount());    }    /**     * This is the options set builder, we can create the options by this method     */    public static class Builder{        private String cacheRootPath;        private int maxFileCount;        private int maxCacheSize;        private boolean isUseFileCache;        public Builder(){        }        public String getCacheRootPath() {            return cacheRootPath;        }        public Builder setCacheRootPath(String cacheRootPath) {            this.cacheRootPath = cacheRootPath;            return this;        }        public int getMaxFileCount() {            return maxFileCount;        }        public Builder setMaxFileCount(int maxFileCount) {            this.maxFileCount = maxFileCount;            return this;        }        public int getMaxCacheSize() {            return maxCacheSize;        }        public Builder setMaxCacheSize(int maxCacheSize) {            this.maxCacheSize = maxCacheSize;            return this;        }        public boolean isUseFileCache() {            return isUseFileCache;        }        public Builder setIsUseFileCache(boolean isUseFileCache) {            this.isUseFileCache = isUseFileCache;            return this;        }        public FileCacheOptions builder(){            return new FileCacheOptions(this);        }    }}

接着就是核心的处理类:
/** * Created by CJstar on 15/8/24. */public class LRUFileCache implements FileCache {    /**     * cache config     */    private FileCacheOptions options;    /**     * cache file suffix     */    private static final String WHOLESALE_CONV = ".cach";    /**     * mini free space on SDCard     */    private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10*1024*1024;    private static LRUFileCache mLRUFileCache;    public static LRUFileCache getInstance(){        if(mLRUFileCache==null){            synchronized (LRUFileCache.class){                if(mLRUFileCache==null){                    mLRUFileCache = new LRUFileCache();                }            }        }        return mLRUFileCache;    }    public void setFileLoadOptions(FileCacheOptions options) {        this.options = options;    }    /**     * use default options     */    private LRUFileCache() {        this.options = new FileCacheOptions.Builder()                .setCacheRootPath("FileCache")                .setIsUseFileCache(true)                .setMaxCacheSize(10 * 1024 * 1024)//10MB                .setMaxFileCount(100)                .builder();    }    @Override    public void addDiskFile(String key, InputStream inputStream) {        if (TextUtils.isEmpty(key) || inputStream == null) {            return;        }        String filename = convertUrlToFileName(key);        String dir = options.getCacheRootPath();        File dirFile = new File(dir);        if (!dirFile.exists())            dirFile.mkdirs();        File file = new File(dir + "/" + filename);        OutputStream outStream;        try {            if(file.exists()){                file.delete();            }                        file.createNewFile();            outStream = new FileOutputStream(file);            while (inputStream.available()!=0){                outStream.write(inputStream.read());            }            outStream.flush();            outStream.close();            inputStream.close();        } catch (Throwable e) {            Log.w("LRUFileCache", e.getMessage());        }        // free the space at every time to add a new file        freeSpaceIfNeeded();    }    @Override    public File getDiskFile(String key) {        File file = new File(getFilePathByKey(key));        if(file!=null&&file.exists()){            updateFileTime(file);        }else{            file = null;        }        return file;    }    @Override    public boolean isExist(String key) {        if (URLUtil.isNetworkUrl(key)) {            return new File(options.getCacheRootPath() + "/" + convertUrlToFileName(key)).exists();        } else if (URLUtil.isFileUrl(key)) {            return new File(key).exists();        } else {            return false;        }    }    @Override    public void removeDiskFile(String key) {        File file = getDiskFile(key);        if (file != null &&file.exists()) {            file.delete();        }    }    @Override    public void removeAllDiskFiles() {        new File(options.getCacheRootPath()).delete();    }    /**     * This method will free the files which had not been used at a long time     */    private void freeSpaceIfNeeded(){        File dir = new File(options.getCacheRootPath());        File[] files = dir.listFiles();        if(files==null){            return;        }        int dirSize = 0;        for (int i = 0; i < files.length; i++) {            if (files[i].getName().contains(WHOLESALE_CONV)) {                dirSize += files[i].length();            }        }        // if the dir size larger than max size or the free space on SDCard is less than 10MB        //free 40% space for system        if (dirSize > options.getMaxCacheSize()                || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {            // delete 40% files by LRU            int removeFactor = (int) ((0.4 * files.length) + 1);            // sort the files by modify time            Arrays.sort(files, new FileLastModifSort());            // delete files            for (int i = 0; i < removeFactor; i++) {                if (files[i].getName().contains(WHOLESALE_CONV)) {                    files[i].delete();                }            }        }        //if file count is larger than max count, delete the last        if(files.length>options.getMaxFileCount()){            Arrays.sort(files, new FileLastModifSort());            // delete files            for (int i = options.getMaxFileCount(); i < files.length; i++) {                if (files[i].getName().contains(WHOLESALE_CONV)) {                    files[i].delete();                }            }        }    }    /**     * Modify the file time     *     * @param file the file which need to update time     */    public void updateFileTime(File file) {        if(file!=null&&file.exists()){            long newModifiedTime = System.currentTimeMillis();            file.setLastModified(newModifiedTime);        }    }    /**     * get the free space on SDCard     *     * @return free size in MB     */    private int freeSpaceOnSd() {        StatFs stat = new StatFs(Environment.getExternalStorageDirectory()                .getPath());        double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat                .getBlockSize());        return (int) sdFreeMB;    }    /**     * Get the file name by file url     *     * @param url     * @return file name     */    private String convertUrlToFileName(String url) {        String[] strs = url.split("/");        return strs[strs.length - 1] + WHOLESALE_CONV;    }    public String getFilePathByKey(String key){        if(URLUtil.isFileUrl(key)){            return key;        }else if(URLUtil.isNetworkUrl(key)){            return options.getCacheRootPath()+"/"+convertUrlToFileName(key);        }else {            return null;        }    }    /**     * The comparator for the file modify, sort the files by modify time.     */    private class FileLastModifSort implements Comparator<File> {        public int compare(File arg0, File arg1) {            if (arg0.lastModified() > arg1.lastModified()) {                return 1;            } else if (arg0.lastModified() == arg1.lastModified()) {                return 0;            } else {                return -1;            }        }    }}

完整的代码地址是:https://github.com/CJstar/Android-ImageFileCache

接下来将讲解MemmoryCache,也是采用LRU算法实现的缓存,不过它比文件缓存复杂一点。

版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案