0
点赞
收藏
分享

微信扫一扫

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

public

super(inputStream);

        }

                                                       

@Override

public long skip(long n) throws

long

while

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

if

int

if

break;  // we reached EOF

else

// we read one byte

                    }

                }

                totalBytesSkipped += bytesSkipped;

            }

return

        }

    }

 

 主界面读取图片


[java]  view plain copy



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



 内存中读取


[java]  view plain copy



  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.                                                                                             
  10. public ImageMemoryCache(Context context) {  
  11. int memClass = ((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();  
  12. int cacheSize = 1024 * 1024 * memClass / 4;  //硬引用缓存容量,为系统可用内存的1/4  
  13. new LruCache<String, Bitmap>(cacheSize) {  
  14. @Override  
  15. protected int sizeOf(String key, Bitmap value) {  
  16. if (value != null)  
  17. return value.getRowBytes() * value.getHeight();  
  18. else  
  19. return 0;  
  20.             }  
  21.                                                                                             
  22. @Override  
  23. protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {  
  24. if (oldValue != null)  
  25. // 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存  
  26. new SoftReference<Bitmap>(oldValue));  
  27.             }  
  28.         };  
  29. new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {  
  30. private static final long serialVersionUID = 6040103833179403725L;  
  31. @Override  
  32. protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {  
  33. if (size() > SOFT_CACHE_SIZE){      
  34. return true;    
  35.                 }    
  36. return false;   
  37.             }  
  38.         };  
  39.     }  
  40.                                                                                     
  41. /**
  42.      * 从缓存中获取图片
  43.      */  
  44. public Bitmap getBitmapFromCache(String url) {  
  45.         Bitmap bitmap;  
  46. //先从硬引用缓存中获取  
  47. synchronized (mLruCache) {  
  48.             bitmap = mLruCache.get(url);  
  49. if (bitmap != null) {  
  50. //如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除  
  51.                 mLruCache.remove(url);  
  52.                 mLruCache.put(url, bitmap);  
  53. return bitmap;  
  54.             }  
  55.         }  
  56. //如果硬引用缓存中找不到,到软引用缓存中找  
  57. synchronized (mSoftCache) {   
  58.             SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);  
  59. if (bitmapReference != null) {  
  60.                 bitmap = bitmapReference.get();  
  61. if (bitmap != null) {  
  62. //将图片移回硬缓存  
  63.                     mLruCache.put(url, bitmap);  
  64.                     mSoftCache.remove(url);  
  65. return bitmap;  
  66. else {  
  67.                     mSoftCache.remove(url);  
  68.                 }  
  69.             }  
  70.         }  
  71. return null;  
  72.     }   
  73.                                                                                     
  74. /**
  75.      * 添加图片到缓存
  76.      */  
  77. public void addBitmapToCache(String url, Bitmap bitmap) {  
  78. if (bitmap != null) {  
  79. synchronized (mLruCache) {  
  80.                 mLruCache.put(url, bitmap);  
  81.             }  
  82.         }  
  83.     }  
  84.                                                                                     
  85. public void clearCache() {  
  86.         mSoftCache.clear();  
  87.     }  
  88. }  


[java]  view plain copy



  1. public class ImageFileCache {  
  2. private static final String CACHDIR = "ImgCach";  
  3. private static final String WHOLESALE_CONV = ".cach";  
  4.                                                               
  5. private static final int MB = 1024*1024;  
  6. private static final int CACHE_SIZE = 10;  
  7. private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;  
  8.                                                                   
  9. public ImageFileCache() {  
  10. //清理文件缓存  
  11.         removeCache(getDirectory());  
  12.     }  
  13.                                                                   
  14. /** 从缓存中获取图片 **/  
  15. public Bitmap getImage(final String url) {      
  16. final String path = getDirectory() + "/" + convertUrlToFileName(url);  
  17. new File(path);  
  18. if (file.exists()) {  
  19.             Bitmap bmp = BitmapFactory.decodeFile(path);  
  20. if (bmp == null) {  
  21.                 file.delete();  
  22. else {  
  23.                 updateFileTime(path);  
  24. return bmp;  
  25.             }  
  26.         }  
  27. return null;  
  28.     }  
  29.                                                                   
  30. /** 将图片存入文件缓存 **/  
  31. public void saveBitmap(Bitmap bm, String url) {  
  32. if (bm == null) {  
  33. return;  
  34.         }  
  35. //判断sdcard上的空间  
  36. if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {  
  37. //SD空间不足  
  38. return;  
  39.         }  
  40.         String filename = convertUrlToFileName(url);  
  41.         String dir = getDirectory();  
  42. new File(dir);  
  43. if (!dirFile.exists())  
  44.             dirFile.mkdirs();  
  45. new File(dir +"/" + filename);  
  46. try {  
  47.             file.createNewFile();  
  48. new FileOutputStream(file);  
  49. 100, outStream);  
  50.             outStream.flush();  
  51.             outStream.close();  
  52. catch (FileNotFoundException e) {  
  53. "ImageFileCache", "FileNotFoundException");  
  54. catch (IOException e) {  
  55. "ImageFileCache", "IOException");  
  56.         }  
  57.     }   
  58.                                                                   
  59. /**
  60.      * 计算存储目录下的文件大小,
  61.      * 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
  62.      * 那么删除40%最近没有被使用的文件
  63.      */  
  64. private boolean removeCache(String dirPath) {  
  65. new File(dirPath);  
  66.         File[] files = dir.listFiles();  
  67. if (files == null) {  
  68. return true;  
  69.         }  
  70. if (!android.os.Environment.getExternalStorageState().equals(  
  71.                 android.os.Environment.MEDIA_MOUNTED)) {  
  72. return false;  
  73.         }  
  74.                                                               
  75. int dirSize = 0;  
  76. for (int i = 0; i < files.length; i++) {  
  77. if (files[i].getName().contains(WHOLESALE_CONV)) {  
  78.                 dirSize += files[i].length();  
  79.             }  
  80.         }  
  81.                                                               
  82. if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {  
  83. int removeFactor = (int) ((0.4 * files.length) + 1);  
  84. new FileLastModifSort());  
  85. for (int i = 0; i < removeFactor; i++) {  
  86. if (files[i].getName().contains(WHOLESALE_CONV)) {  
  87.                     files[i].delete();  
  88.                 }  
  89.             }  
  90.         }  
  91.                                                               
  92. if (freeSpaceOnSd() <= CACHE_SIZE) {  
  93. return false;  
  94.         }  
  95.                                                                       
  96. return true;  
  97.     }  
  98.                                                                   
  99. /** 修改文件的最后修改时间 **/  
  100. public void updateFileTime(String path) {  
  101. new File(path);  
  102. long newModifiedTime = System.currentTimeMillis();  
  103.         file.setLastModified(newModifiedTime);  
  104.     }  
  105.                                                                   
  106. /** 计算sdcard上的剩余空间 **/  
  107. private int freeSpaceOnSd() {  
  108. new StatFs(Environment.getExternalStorageDirectory().getPath());  
  109. double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;  
  110. return (int) sdFreeMB;  
  111.     }   
  112.                                                                   
  113. /** 将url转成文件名 **/  
  114. private String convertUrlToFileName(String url) {  
  115. "/");  
  116. return strs[strs.length - 1] + WHOLESALE_CONV;  
  117.     }  
  118.                                                                   
  119. /** 获得缓存目录 **/  
  120. private String getDirectory() {  
  121. "/" + CACHDIR;  
  122. return dir;  
  123.     }  
  124.                                                                   
  125. /** 取SD卡路径 **/  
  126. private String getSDPath() {  
  127. null;  
  128. boolean sdCardExist = Environment.getExternalStorageState().equals(  
  129. //判断sd卡是否存在  
  130. if (sdCardExist) {  
  131. //获取根目录  
  132.         }  
  133. if (sdDir != null) {  
  134. return sdDir.toString();  
  135. else {  
  136. return "";  
  137.         }  
  138.     }   
  139.                                                               
  140. /**
  141.      * 根据文件的最后修改时间进行排序
  142.      */  
  143. private class FileLastModifSort implements Comparator<File> {  
  144. public int compare(File arg0, File arg1) {  
  145. if (arg0.lastModified() > arg1.lastModified()) {  
  146. return 1;  
  147. else if (arg0.lastModified() == arg1.lastModified()) {  
  148. return 0;  
  149. else {  
  150. return -1;  
  151.             }  
  152.         }  
  153.     }  
  154.                                                               
  155. }  



网络下载图片


[java]  view plain copy



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




 权限


[html]  view plain copy



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

相关推荐

0 条评论