本文转自:http://blog.csdn.net/a79412906/article/details/10180583

Android每次加载图片很浪费时间。所以设计了一个图片缓存技术来解决每次android手机加载图片的问题

内存的读取速度是最快的,然后是文件的读取速度,最后是网络资源的读取

既然内存的读取时间最快,我们好好利用内存资源。将内存再分两层缓存

强引用缓存不会轻易被回收,来保存常用数据,不常用的资源放入软引用缓存中。

对于硬引用和软引用的介绍:

⑴强引用(StrongReference)
    强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

⑵软引用(SoftReference)

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存

 1,对于强引用和软引用的使用,我们首先去强引用缓存中去找图片资源,当没有发现时,就去软引用缓存中。当强引用的超额时,将最后使用的资源放入软引用缓存中,使用到软引用的资源时 ,则将资源重新放回强引用缓存池中。

 2,内存缓存池中找不到,就去文件中查找,

 

3,再找不到就只好去网络上下载了,下载到以后,分别将资源放入文件缓存和内存缓存中

 

Android对于InputStream流有个小bug在慢速网络的情况下可能产生中断,可以考虑重写FilterInputStream处理
skip方法来解决这个bug。 BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法获取完整的数据,这里我 们通
过继承FilterInputStream类的skip方法来强制实现flush流中的数据,主要原理就是检查是否到文件末端,告诉http类是否继续。

static class FlushedInputStream extends FilterInputStream {

public FlushedInputStream(InputStream inputStream) {

super(inputStream);

}

@Override

public long skip(long n) throws IOException {

long totalBytesSkipped = 0L;

while (totalBytesSkipped < n) {

long bytesSkipped = in.skip(n - totalBytesSkipped);

if (bytesSkipped == 0L) {

int b = read();

if (b < 0) {

break;  // we reached EOF

} else {

bytesSkipped = 1; // we read one byte

}

}

totalBytesSkipped += bytesSkipped;

}

return totalBytesSkipped;

}

}

主界面读取图片

  1. public class MainActivity extends Activity {
  2. private ImageMemoryCache memoryCache;
  3. private ImageFileCache fileCache;
  4. private ImageView imageView;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. memoryCache=new ImageMemoryCache(this);
  10. fileCache=new ImageFileCache();
  11. imageView=(ImageView) findViewById(R.id.img);
  12. Bitmap b=getBitmap("http://f.hiphotos.baidu.com/album/w%3D2048/sign=7aa167f79f2f07085f052d00dd1cb999/472309f7905298228f794c7bd6ca7bcb0b46d4c4.jpg");
  13. imageView.setImageBitmap(b);
  14. }
  15. public Bitmap getBitmap(String url) {
  16. // 从内存缓存中获取图片
  17. Bitmap result = memoryCache.getBitmapFromCache(url);
  18. if (result == null) {
  19. // 文件缓存中获取
  20. result = fileCache.getImage(url);
  21. if (result == null) {
  22. // 从网络获取
  23. result = ImageGetFromHttp.downloadBitmap(url);
  24. if (result != null) {
  25. fileCache.saveBitmap(result, url);
  26. memoryCache.addBitmapToCache(url, result);
  27. }
  28. } else {
  29. // 添加到内存缓存
  30. memoryCache.addBitmapToCache(url, result);
  31. }
  32. }
  33. return result;
  34. }
  35. }

内存中读取

  1. public class ImageMemoryCache {
  2. /**
  3. * 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。
  4. * 硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。
  5. */
  6. private static final int SOFT_CACHE_SIZE = 15;  //软引用缓存容量
  7. private static LruCache<String, Bitmap> mLruCache;  //硬引用缓存
  8. private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache;  //软引用缓存
  9. public ImageMemoryCache(Context context) {
  10. int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
  11. int cacheSize = 1024 * 1024 * memClass / 4;  //硬引用缓存容量,为系统可用内存的1/4
  12. mLruCache = new LruCache<String, Bitmap>(cacheSize) {
  13. @Override
  14. protected int sizeOf(String key, Bitmap value) {
  15. if (value != null)
  16. return value.getRowBytes() * value.getHeight();
  17. else
  18. return 0;
  19. }
  20. @Override
  21. protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
  22. if (oldValue != null)
  23. // 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存
  24. mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
  25. }
  26. };
  27. mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {
  28. private static final long serialVersionUID = 6040103833179403725L;
  29. @Override
  30. protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
  31. if (size() > SOFT_CACHE_SIZE){
  32. return true;
  33. }
  34. return false;
  35. }
  36. };
  37. }
  38. /**
  39. * 从缓存中获取图片
  40. */
  41. public Bitmap getBitmapFromCache(String url) {
  42. Bitmap bitmap;
  43. //先从硬引用缓存中获取
  44. synchronized (mLruCache) {
  45. bitmap = mLruCache.get(url);
  46. if (bitmap != null) {
  47. //如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除
  48. mLruCache.remove(url);
  49. mLruCache.put(url, bitmap);
  50. return bitmap;
  51. }
  52. }
  53. //如果硬引用缓存中找不到,到软引用缓存中找
  54. synchronized (mSoftCache) {
  55. SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
  56. if (bitmapReference != null) {
  57. bitmap = bitmapReference.get();
  58. if (bitmap != null) {
  59. //将图片移回硬缓存
  60. mLruCache.put(url, bitmap);
  61. mSoftCache.remove(url);
  62. return bitmap;
  63. } else {
  64. mSoftCache.remove(url);
  65. }
  66. }
  67. }
  68. return null;
  69. }
  70. /**
  71. * 添加图片到缓存
  72. */
  73. public void addBitmapToCache(String url, Bitmap bitmap) {
  74. if (bitmap != null) {
  75. synchronized (mLruCache) {
  76. mLruCache.put(url, bitmap);
  77. }
  78. }
  79. }
  80. public void clearCache() {
  81. mSoftCache.clear();
  82. }
  83. }
  1. public class ImageFileCache {
  2. private static final String CACHDIR = "ImgCach";
  3. private static final String WHOLESALE_CONV = ".cach";
  4. private static final int MB = 1024*1024;
  5. private static final int CACHE_SIZE = 10;
  6. private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;
  7. public ImageFileCache() {
  8. //清理文件缓存
  9. removeCache(getDirectory());
  10. }
  11. /** 从缓存中获取图片 **/
  12. public Bitmap getImage(final String url) {
  13. final String path = getDirectory() + "/" + convertUrlToFileName(url);
  14. File file = new File(path);
  15. if (file.exists()) {
  16. Bitmap bmp = BitmapFactory.decodeFile(path);
  17. if (bmp == null) {
  18. file.delete();
  19. } else {
  20. updateFileTime(path);
  21. return bmp;
  22. }
  23. }
  24. return null;
  25. }
  26. /** 将图片存入文件缓存 **/
  27. public void saveBitmap(Bitmap bm, String url) {
  28. if (bm == null) {
  29. return;
  30. }
  31. //判断sdcard上的空间
  32. if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
  33. //SD空间不足
  34. return;
  35. }
  36. String filename = convertUrlToFileName(url);
  37. String dir = getDirectory();
  38. File dirFile = new File(dir);
  39. if (!dirFile.exists())
  40. dirFile.mkdirs();
  41. File file = new File(dir +"/" + filename);
  42. try {
  43. file.createNewFile();
  44. OutputStream outStream = new FileOutputStream(file);
  45. bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
  46. outStream.flush();
  47. outStream.close();
  48. } catch (FileNotFoundException e) {
  49. Log.w("ImageFileCache", "FileNotFoundException");
  50. } catch (IOException e) {
  51. Log.w("ImageFileCache", "IOException");
  52. }
  53. }
  54. /**
  55. * 计算存储目录下的文件大小,
  56. * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
  57. * 那么删除40%最近没有被使用的文件
  58. */
  59. private boolean removeCache(String dirPath) {
  60. File dir = new File(dirPath);
  61. File[] files = dir.listFiles();
  62. if (files == null) {
  63. return true;
  64. }
  65. if (!android.os.Environment.getExternalStorageState().equals(
  66. android.os.Environment.MEDIA_MOUNTED)) {
  67. return false;
  68. }
  69. int dirSize = 0;
  70. for (int i = 0; i < files.length; i++) {
  71. if (files[i].getName().contains(WHOLESALE_CONV)) {
  72. dirSize += files[i].length();
  73. }
  74. }
  75. if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
  76. int removeFactor = (int) ((0.4 * files.length) + 1);
  77. Arrays.sort(files, new FileLastModifSort());
  78. for (int i = 0; i < removeFactor; i++) {
  79. if (files[i].getName().contains(WHOLESALE_CONV)) {
  80. files[i].delete();
  81. }
  82. }
  83. }
  84. if (freeSpaceOnSd() <= CACHE_SIZE) {
  85. return false;
  86. }
  87. return true;
  88. }
  89. /** 修改文件的最后修改时间 **/
  90. public void updateFileTime(String path) {
  91. File file = new File(path);
  92. long newModifiedTime = System.currentTimeMillis();
  93. file.setLastModified(newModifiedTime);
  94. }
  95. /** 计算sdcard上的剩余空间 **/
  96. private int freeSpaceOnSd() {
  97. StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
  98. double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;
  99. return (int) sdFreeMB;
  100. }
  101. /** 将url转成文件名 **/
  102. private String convertUrlToFileName(String url) {
  103. String[] strs = url.split("/");
  104. return strs[strs.length - 1] + WHOLESALE_CONV;
  105. }
  106. /** 获得缓存目录 **/
  107. private String getDirectory() {
  108. String dir = getSDPath() + "/" + CACHDIR;
  109. return dir;
  110. }
  111. /** 取SD卡路径 **/
  112. private String getSDPath() {
  113. File sdDir = null;
  114. boolean sdCardExist = Environment.getExternalStorageState().equals(
  115. android.os.Environment.MEDIA_MOUNTED);  //判断sd卡是否存在
  116. if (sdCardExist) {
  117. sdDir = Environment.getExternalStorageDirectory();  //获取根目录
  118. }
  119. if (sdDir != null) {
  120. return sdDir.toString();
  121. } else {
  122. return "";
  123. }
  124. }
  125. /**
  126. * 根据文件的最后修改时间进行排序
  127. */
  128. private class FileLastModifSort implements Comparator<File> {
  129. public int compare(File arg0, File arg1) {
  130. if (arg0.lastModified() > arg1.lastModified()) {
  131. return 1;
  132. } else if (arg0.lastModified() == arg1.lastModified()) {
  133. return 0;
  134. } else {
  135. return -1;
  136. }
  137. }
  138. }
  139. }

网络下载图片

  1. public class ImageGetFromHttp {
  2. private static final String LOG_TAG = "ImageGetFromHttp";
  3. public static Bitmap downloadBitmap(String url) {
  4. final HttpClient client = new DefaultHttpClient();
  5. final HttpGet getRequest = new HttpGet(url);
  6. try {
  7. HttpResponse response = client.execute(getRequest);
  8. final int statusCode = response.getStatusLine().getStatusCode();
  9. if (statusCode != HttpStatus.SC_OK) {
  10. Log.w(LOG_TAG, "Error " + statusCode + " while retrieving bitmap from " + url);
  11. return null;
  12. }
  13. final HttpEntity entity = response.getEntity();
  14. if (entity != null) {
  15. InputStream inputStream = null;
  16. try {
  17. inputStream = entity.getContent();
  18. FilterInputStream fit = new FlushedInputStream(inputStream);
  19. return BitmapFactory.decodeStream(fit);
  20. } finally {
  21. if (inputStream != null) {
  22. inputStream.close();
  23. inputStream = null;
  24. }
  25. entity.consumeContent();
  26. }
  27. }
  28. } catch (IOException e) {
  29. getRequest.abort();
  30. Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
  31. } catch (IllegalStateException e) {
  32. getRequest.abort();
  33. Log.w(LOG_TAG, "Incorrect URL: " + url);
  34. } catch (Exception e) {
  35. getRequest.abort();
  36. Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
  37. } finally {
  38. client.getConnectionManager().shutdown();
  39. }
  40. return null;
  41. }
  42. /*
  43. * An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
  44. */
  45. static class FlushedInputStream extends FilterInputStream {
  46. public FlushedInputStream(InputStream inputStream) {
  47. super(inputStream);
  48. }
  49. @Override
  50. public long skip(long n) throws IOException {
  51. long totalBytesSkipped = 0L;
  52. while (totalBytesSkipped < n) {
  53. long bytesSkipped = in.skip(n - totalBytesSkipped);
  54. if (bytesSkipped == 0L) {
  55. int b = read();
  56. if (b < 0) {
  57. break;  // we reached EOF
  58. } else {
  59. bytesSkipped = 1; // we read one byte
  60. }
  61. }
  62. totalBytesSkipped += bytesSkipped;
  63. }
  64. return totalBytesSkipped;
  65. }
  66. }
  67. }

权限

    1. <uses-permission android:name="android.permission.INTERNET" />
    2. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

android 网络图片双缓存的更多相关文章

  1. Android中用双缓存技术,加载网络图片

    最近在学校参加一个比赛,写的一个Android应用,里面要加载大量的网络图片,可是用传统的方法图片一多就会造成程序出现内存溢出而崩溃.因为自己也在学习中,所以看了很多博客和视频,然后参照这些大神的写源 ...

  2. android 双缓存机制

    废话不多说,直接贴代码! 所谓的双缓存,第一就是缓存在内存里面,第二就是缓存在SD卡里面,当你需要加载数据时,先去内存缓存中查找,如果没有再去SD卡中查找,并且用户可以自选使用哪种缓存! 缓存内存和缓 ...

  3. Android 框架修炼-自己封装双缓存管理框架库

    一.概述 Android开发中,网络请求是很重要的一部分,而缓存网络请求来的图片或者响应结果字符串或者结果流,既可以省流量,同时也可以帮助我们 解决无网或弱网情况下加载情况,当然也可以提升程序性能效率 ...

  4. wemall app商城源码Android之ListView异步加载网络图片(优化缓存机制)

    wemall-mobile是基于WeMall的android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享wemall app商城源码Android之L ...

  5. Android为TV端助力 双缓存机制

    废话不多说,直接贴代码! 所谓的双缓存,第一就是缓存在内存里面,第二就是缓存在SD卡里面,当你需要加载数据时,先去内存缓存中查找,如果没有再去SD卡中查找,并且用户可以自选使用哪种缓存! 缓存内存和缓 ...

  6. Android自定义view双缓存技术

    自定义一个写字板来帮助理解双缓存.如果不使用双缓存那么用户只能看到绘制的最后一笔的效果.因为在不断调用invalidate(),未保存的绘制过程会消失. package newviews; impor ...

  7. (BUG已修改,最优化)安卓ListView异步加载网络图片与缓存软引用图片,线程池,只加载当前屏之说明

    原文:http://blog.csdn.net/java_jh/article/details/20068915 迟点出更新的.这个还有BUG.因为软引应不给力了.2.3之后 前几天的原文有一个线程管 ...

  8. picasso_强大的Android图片下载缓存库

    tag: android pic skill date: 2016/07/09 title: picasso-强大的Android图片下载缓存库 [本文转载自:泡在网上的日子 参考:http://bl ...

  9. android图片的缓存--节约内存提高程序效率

    如今android应用占内存一个比一个大,android程序的质量亟待提高. 这里简单说说网络图片的缓存,我这边就简单的说说思路 1:网络图片,无疑须要去下载图片,我们不须要每次都去下载. 维护一张表 ...

随机推荐

  1. Linux下如何查看系统启动时间和运行时间(转)

    1.uptime命令输出:16:11:40 up 59 days, 4:21, 2 users, load average: 0.00, 0.01, 0.00 2.查看/proc/uptime文件计算 ...

  2. PLSQL连接Oracle 数据库配置详解

    1. 下载instantclient-basic-win32-11.2.0.1.0 (oracle官网下载地址:http://www.oracle.com/technetwork/topics/win ...

  3. [Android Pro] AndroidX了解一下

    cp : https://blog.csdn.net/qq_17766199/article/details/81433706 1.说明 官方原文如下: We hope the division be ...

  4. ESLint 配置说明

    ESLint 有什么用,为什么要使用?   ESLint是一套可自定义规则的JS代码检查与修复工具 目标是保存团队代码的一致性和避免错误并且修复错误.减少团队沟通成本   "no-alert ...

  5. iOS 7设计备忘单

    With the release of iOS 7, app designers and developers will need to adjust their visual language to ...

  6. 浅谈常用的几种web攻击方式

    一.Dos攻击(Denial of Service attack) 是一种针对服务器的能够让服务器呈现静止状态的攻击方式.有时候也加服务停止攻击或拒绝服务攻击.其原理就是发送大量的合法请求到服务器,服 ...

  7. java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁(转载)

    之前做过一个测试,详情见这篇文章<多线程 +1操作的几种实现方式,及效率对比>,当时对这个测试结果很疑惑,反复执行过多次,发现结果是一样的: 1. 单线程下synchronized效率最高 ...

  8. PNG、 JPG图片压缩方法

    参考链接 https://tinypng.com/developers/reference/python 1.安装 pip install --upgrade tinify 2.使用python脚本压 ...

  9. 最好的Python机器学习库

    参考链接:http://www.csdn.net/article/2015-12-10/2826435

  10. T-Pot平台cowrie蜜罐暴力破解探测及实现自动化邮件告警

    前言:Cowrie是基于kippo更改的中交互ssh蜜罐, 可以对暴力攻击账号密码等记录,并提供伪造的文件系统环境记录黑客操作行为, 并保存通过wget/curl下载的文件以及通过SFTP.SCP上传 ...