編輯:關於Android編程
在博客:Android大圖加載內存優化(如何防止OutOfMemmory)中講解了在加載圖片的時候內存不完全加載原圖或預估圖片的大小,加載合適的尺寸的圖片防止OOM。接下來講解圖片文件的本地緩存,網絡圖片必須經過本地緩存,才能提高資源的訪問速度,內存的緩存必須配合SDCard的緩存,才能發揮它的優勢。本文采用的是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{ 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; } } } }
有的時候我們需要為一個listview設置固定的數據,下邊就是如何設置靜態的數據布局文件listview 的主頁面 然後的一個布局文件為每一個listview的item,
在上一篇文章中我帶著大家一起實現了Android瀑布流照片牆的效果,雖然這種效果很炫很酷,但其實還只能算是一個半成品,因為照片牆中所有的圖片都是只能看不能點的。因此本篇文
功能1、顯示加載視圖,加載失敗的時候顯示加載失敗視圖,數據為空時顯示數據為空視圖,支持為失敗視圖設置點擊事件重新加載數據。2、支持個性化設置,自定義設置 加載、失敗、空數
在tv上開發gridview有焦點放大這個效果還是很普遍的做法,今天就講下這個實現方案,當然要實現這個效果有很多種,我這裡只是講其中的一種實現方案,也是比較簡單而且容易看