本文主要包括以下内容

  1. 内存缓存策略
  2. 文件缓存策略

内存缓存策略

当有一个图片要去从网络下载的时候,我们并不会直接去从网络下载,因为在这个时代,用户的流量是宝贵的,耗流量的应用是不会得到用户的青睐的。那我们该怎么办呢?这样,我们会先从内存缓存中去查找是否有该图片,如果没有就去文件缓存中查找是否有该图片,如果还没有,我们就从网络下载图片。本博文的侧重点是如何做内存缓存,内存缓存的查找策略是:先从强引用缓存中查找,如果没有再从软引用缓存中查找,如果在软引用缓存中找到了,就把它移入强引用缓存;如果强引用缓存满了,就会根据Lru算法把某些图片移入软引用缓存,如果软引用缓存也满了,最早的软引用就会被删除。这里,我有必要说明下几个概念:强引用、软引用、弱引用、Lru。

  • 强引用:就是直接引用一个对象,一般的对象引用均是强引用
  • 软引用:引用一个对象,当内存不足并且除了我们的引用之外没有其他地方引用此对象的情况 下,该对象会被gc回收
  • 弱引用:引用一个对象,当除了我们的引用之外没有其他地方引用此对象的情况下,只要gc被调用,它就会被回收(请注意它和软引用的区别)
  • Lru:Least Recently Used 近期最少使用算法,是一种页面置换算法,其思想是在缓存的页面数目固定的情况下,那些最近使用次数最少的页面将被移出,对于我们的内存缓存来说,强引用缓存大小固定为4M,如果当缓存的图片大于4M的时候,有些图片就会被从强引用缓存中删除,哪些图片会被删除呢,就是那些近期使用次数最少的图片。

注意

In the past, a popular memory cache implementation was a SoftReference or WeakReference bitmap cache, however this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more aggressive with collecting soft/weak references which makes them fairly ineffective. In addition, prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.

在android2.3之后,软引用与弱引用已经不可靠了,所以慎用。

public class ImageMemoryCache {/*** 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。*  强引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。*/private static final String TAG = "ImageMemoryCache";private static LruCache<String, Bitmap> mLruCache; // 强引用缓存private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; // 软引用缓存private static final int LRU_CACHE_SIZE = 4 * 1024 * 1024; // 强引用缓存容量:4MBprivate static final int SOFT_CACHE_NUM = 20; // 软引用缓存个数// 在这里分别初始化强引用缓存和弱引用缓存public ImageMemoryCache() {mLruCache = new LruCache<String, Bitmap>(LRU_CACHE_SIZE) {@Override// sizeOf返回为单个hashmap value的大小protected int sizeOf(String key, Bitmap value) {if (value != null)return value.getRowBytes() * value.getHeight();elsereturn 0;}@Overrideprotected void entryRemoved(boolean evicted, String key,Bitmap oldValue, Bitmap newValue) {if (oldValue != null) {// 强引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存Logger.d(TAG, "LruCache is full,move to SoftRefernceCache");mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));}}};mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_NUM, 0.75f, true) {private static final long serialVersionUID = 1L;/*** 当软引用数量大于20的时候,最旧的软引用将会被从链式哈希表中移出*/@Overrideprotected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {if (size() > SOFT_CACHE_NUM) {Logger.d(TAG, "should remove the eldest from SoftReference");return true;}return false;}};}/*** 从缓存中获取图片*/public Bitmap getBitmapFromMemory(String url) {Bitmap bitmap;// 先从强引用缓存中获取synchronized (mLruCache) {bitmap = mLruCache.get(url);if (bitmap != null) {// 如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除mLruCache.remove(url);mLruCache.put(url, bitmap);Logger.d(TAG, "get bmp from LruCache,url=" + url);return bitmap;}}// 如果强引用缓存中找不到,到软引用缓存中找,找到后就把它从软引用中移到强引用缓存中synchronized (mSoftCache) {SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);if (bitmapReference != null) {bitmap = bitmapReference.get();if (bitmap != null) {// 将图片移回LruCachemLruCache.put(url, bitmap);mSoftCache.remove(url);Logger.d(TAG, "get bmp from SoftReferenceCache, url=" + url);return bitmap;} else {mSoftCache.remove(url);}}}return null;}/*** 添加图片到缓存*/public void addBitmapToMemory(String url, Bitmap bitmap) {if (bitmap != null) {synchronized (mLruCache) {mLruCache.put(url, bitmap);}}}public void clearCache() {mSoftCache.clear();}
}

文件缓存策略

当一张图片从网络下载成功以后,这个图片会被加入内存缓存和文件缓存,对于文件缓存来说,这张图片将被以url的哈希值加cach后缀名的形式存储在SD卡上,这样,当下一次再需要同一个url的图片的时候,就不需要从网络下载了,而是直接通过url来进行查找。同时一张图片被访问时,它的最后修改时间将被更新,这样的意义在于:当SD卡空间不足的时候,将会按照最后修改时间来删除40%缓存的图片,确切来说,那些修改时间比较早的图片将会被删除。

public class ImageFileCache
{private static final String TAG = "ImageFileCache";//图片缓存目录private static final String IMGCACHDIR = "/sdcard/ImgCach";//保存的cache文件宽展名private static final String CACHETAIL = ".cach";private static final int MB = 1024*1024;private static final int CACHE_SIZE = 1;//当SD卡剩余空间小于10M的时候会清理缓存private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;public ImageFileCache() {//清理部分文件缓存removeCache(IMGCACHDIR);            }/** * 从缓存中获取图片 */public Bitmap getImageFromFile(final String url) {    final String path = IMGCACHDIR + "/" + convertUrlToFileName(url);File file = new File(path);if (file != null && file.exists()) {Bitmap bmp = BitmapFactory.decodeFile(path);if (bmp == null) {file.delete();} else {updateFileTime(path);Logger.d(TAG, "get bmp from FileCache,url=" + url);return bmp;}}return null;}/*** 将图片存入文件缓存 */public void saveBitmapToFile(Bitmap bm, String url) {if (bm == null) {return;}//判断sdcard上的空间if (FREE_SD_SPACE_NEEDED_TO_CACHE > SdCardFreeSpace()) {//SD空间不足return;}String filename = convertUrlToFileName(url);File dirFile = new File(IMGCACHDIR);if (!dirFile.exists())dirFile.mkdirs();File file = new File(IMGCACHDIR +"/" + filename);try {file.createNewFile();OutputStream outStream = new FileOutputStream(file);bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);outStream.flush();outStream.close();} catch (FileNotFoundException e) {Logger.d(TAG, "FileNotFoundException");} catch (IOException e) {Logger.d(TAG, "IOException");}} /*** 计算存储目录下的文件大小,* 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定* 那么删除40%最近没有被使用的文件*/private boolean removeCache(String dirPath) {File dir = new File(dirPath);File[] files = dir.listFiles();if (files == null) {return true;}if (!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){return false;}int dirSize = 0;for (int i = 0; i < files.length; i++) {if (files[i].getName().contains(CACHETAIL)) {dirSize += files[i].length();}}if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > SdCardFreeSpace()) {int removeFactor = (int) (0.4 * files.length);Arrays.sort(files, new FileLastModifSort());for (int i = 0; i < removeFactor; i++) {if (files[i].getName().contains(CACHETAIL)) {files[i].delete();}}}if (SdCardFreeSpace() <= CACHE_SIZE) {return false;}return true;}/*** 修改文件的最后修改时间*/public void updateFileTime(String path) {File file = new File(path);long newModifiedTime = System.currentTimeMillis();file.setLastModified(newModifiedTime);}/** * 计算SD卡上的剩余空间 */private int SdCardFreeSpace(){StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;return (int) sdFreeMB;} /** * 将url转成文件名 */private String convertUrlToFileName(String url){return url.hashCode() + CACHETAIL;}/*** 根据文件的最后修改时间进行排序*/private class FileLastModifSort implements Comparator<File> {public int compare(File file0, File file1) {if (file0.lastModified() > file1.lastModified()){return 1;} else if (file0.lastModified() == file1.lastModified()) {return 0;} else {return -1;}}}}

References

android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略

android中图片的三级cache策略(内存、文件、网络)之三:文件缓存策略

Android缓存学习入门(二)相关推荐

  1. Android缓存学习入门

    本文主要包括以下内容 利用LruCache实现内存缓存 利用DiskLruCache实现磁盘缓存 LruCache与DiskLruCache结合实例 利用了缓存机制的瀑布流实例 内存缓存的实现 pub ...

  2. Android动画学习记录二(属性动画、估值器和插值器)

    Android动画学习记录二(属性动画.估值期和插值器) Android动画学习记录二(属性动画.估值期和插值器) Android动画学习记录二(属性动画.估值期和插值器) 一.补间动画缺陷 二.属性 ...

  3. Android FrameWork学习(二)Android系统源码调试

    点击打开链接 通过上一篇 Android FrameWork学习(一)Android 7.0系统源码下载\编译 我们了解了如何进行系统源码的下载和编译工作. 为了更进一步地学习跟研究 Android ...

  4. Android MediaProjection学习(二)之和MediaRecorder实现录屏

    预备知识 Android MediaProjection学习(一)之实现屏幕截图 通过预备知识我们应该知道MediaProjection的相关方法和知识.在屏幕截图里我们用了ImageReader来获 ...

  5. android studio 学习入门篇

    开发第一应用 可以开发属于自己的应用,是否有点小激动?好吧!让我们开始,首先点击Start a new Android Studio Project创建工程: 接下来需要输入应用名称(第一个字母要大写 ...

  6. Android NDK学习(二):编译脚本语法Android.mk和Application.mk

    一.Android.mk Android.mk分为一下几部分: LOCAL_PATH:= $(call my-dir), 返回当前文件在系统中的路径,Android.mk文件开始时必须定义该变量. i ...

  7. 【安卓开发】android studio 学习入门篇

    以下内容转载自:https://blog.csdn.net/myosotis5/article/details/79208707 (为防止链接失效,特备份文章内容,侵删) 开发第一应用 可以开发属于自 ...

  8. Android视频学习(二):数据存储与界面展示2

    主要包括: Pull解析xml文件 测试 单元测试 junit SQLite数据库 数据库的增删改查 把数据库的数据显示至屏幕 ListView Pull解析xml文件 先自己写一个xml文件,存一些 ...

  9. Android基础学习(二)—— 常用控件

    TextView 显示文本内容 Button 按钮 EditText 输入框 ImageView 显示图片 ProgressBar 进度条 ToolBar 自定义标题栏 AlertDialog 对话框 ...

最新文章

  1. 一步步打造漂亮的新闻列表(无刷新分页、内容预览)(4)
  2. C#反射(Reflection)详解
  3. 浙江高校计算机等级考试二级办公,浙江省高校计算机等级考试二级(高级办公)试题12.ppt...
  4. ApplicationEventPublisherAware--待完善
  5. Linux调优方案,sysctl.conf的设置
  6. latex大写运算符号
  7. scws sphinx mysql_Sphinx+Scws 搭建千万级准实时搜索应用场景详解
  8. (百度云服务器的)域名不加www可以访问,添加就不能访问 (添加域名绑定)- 解决篇
  9. 论文集 | 精选133篇知识图谱论文
  10. HDU 5752 Sqrt Bo【枚举,大水题】
  11. [译] 第二天:AngularJS - 认识AngularJS
  12. 孪生网络图像相似度_CVPR-2019 更深更宽的孪生网络实时追踪
  13. Springboot+jwt+shiro实现用户权限控制
  14. 魔兽世界插件开发-WOWLUA插件
  15. Centos 7 安装Redis
  16. React(四):Smart组件与Dumb组件
  17. 红米k30s至尊纪念版发布会直播地址红米k30s至尊纪念版发布会在线观看入口
  18. python海龟漂亮图案代码大全_带有海龟图案的Python花
  19. 蓝桥杯物联网STM32 学习记录3——HAL库GPIO函数
  20. 离散卷积过程举例图示详解

热门文章

  1. 用多模态信息做 prompt,解锁 GPT 新玩法
  2. 深入浅出不可思议的中文分词技术
  3. 【论文翻译】HeteSim:异构网络中相关性度量的通用框架
  4. .依存句法分析--提取用户评论
  5. 玩转算法之面试第九章-动态规划
  6. 百度作业帮-产品分析
  7. 2020年工业互联网行业研究报告(国盛证券)
  8. Java栈内存、堆内存、方法区对对象和引用的关系
  9. day45-前端CSS
  10. 16重新安装HA0.63