Android big image loading Optimization-local file caching Based on LRU Algorithm

Source: Internet
Author: User

Android big image loading Optimization-local file caching Based on LRU Algorithm
Preface

In his blog: Memory Optimization for Android large image loading (how to prevent OutOfMemmory), I explained how to load the original image or estimate the image size when the memory is not fully loaded, load images of proper sizes to prevent OOM. Next, we will explain the local cache of image files. Network images must be cached locally to speed up resource access. The cache of memory must work with the SDCard cache to take advantage of it. This article uses the LRU local cache policy. This article focuses on file caching, so it does not introduce memory caching or take advantage of image loading from the previous blog, however, in subsequent blogs, I will constantly improve the entire project and lead everyone to reveal the secrets of third-party image loading libraries.

LRU Algorithm

Another important step in image loading is the local cache of network images. In many cases, you do not know when to delete cached images, at this time, a reasonable local image Cache Policy is required to ensure that image files do not occupy Unlimited storage space, resulting in insufficient storage space and resource waste. In the computer operating system, the LRU algorithm is introduced for task scheduling. In general, it is to put the priority of unused resources in the longest period to the lowest level, and ensure that resources with high usage frequency are given priority.

Add a configuration class for the core code of the local image cache. You can configure parameters such as the cache location and size of the image file based on this class. The configuration may be extended in the future.
/** * 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);        }    }}

Next, the core processing class:
/** * 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
 
   {        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;            }        }    }}
 
 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.