本文转自: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类是否继续。

staticclassFlushedInputStreamextendsFilterInputStream{

publicFlushedInputStream(InputStreaminputStream){

super(inputStream);

}

@Override

publiclongskip(longn)throwsIOException{

longtotalBytesSkipped=0L;

while(totalBytesSkipped<n){

longbytesSkipped=in.skip(n-totalBytesSkipped);

if(bytesSkipped==0L){

intb=read();

if(b<0){

break;//wereachedEOF

}else{

bytesSkipped=1;//wereadonebyte

}

}

totalBytesSkipped+=bytesSkipped;

}

returntotalBytesSkipped;

}

}

主界面读取图片

[java] view plain copy
  1. publicclassMainActivityextendsActivity{
  2. privateImageMemoryCachememoryCache;
  3. privateImageFileCachefileCache;
  4. privateImageViewimageView;
  5. @Override
  6. protectedvoidonCreate(BundlesavedInstanceState){
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.main);
  9. memoryCache=newImageMemoryCache(this);
  10. fileCache=newImageFileCache();
  11. imageView=(ImageView)findViewById(R.id.img);
  12. Bitmapb=getBitmap("http://f.hiphotos.baidu.com/album/w%3D2048/sign=7aa167f79f2f07085f052d00dd1cb999/472309f7905298228f794c7bd6ca7bcb0b46d4c4.jpg");
  13. imageView.setImageBitmap(b);
  14. }
  15. publicBitmapgetBitmap(Stringurl){
  16. //从内存缓存中获取图片
  17. Bitmapresult=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. returnresult;
  34. }
  35. }

内存中读取

[java] view plain copy
  1. publicclassImageMemoryCache{
  2. /**
  3. *从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。
  4. *硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。
  5. */
  6. privatestaticfinalintSOFT_CACHE_SIZE=15;//软引用缓存容量
  7. privatestaticLruCache<String,Bitmap>mLruCache;//硬引用缓存
  8. privatestaticLinkedHashMap<String,SoftReference<Bitmap>>mSoftCache;//软引用缓存
  9. publicImageMemoryCache(Contextcontext){
  10. intmemClass=((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
  11. intcacheSize=1024*1024*memClass/4;//硬引用缓存容量,为系统可用内存的1/4
  12. mLruCache=newLruCache<String,Bitmap>(cacheSize){
  13. @Override
  14. protectedintsizeOf(Stringkey,Bitmapvalue){
  15. if(value!=null)
  16. returnvalue.getRowBytes()*value.getHeight();
  17. else
  18. return0;
  19. }
  20. @Override
  21. protectedvoidentryRemoved(booleanevicted,Stringkey,BitmapoldValue,BitmapnewValue){
  22. if(oldValue!=null)
  23. //硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存
  24. mSoftCache.put(key,newSoftReference<Bitmap>(oldValue));
  25. }
  26. };
  27. mSoftCache=newLinkedHashMap<String,SoftReference<Bitmap>>(SOFT_CACHE_SIZE,0.75f,true){
  28. privatestaticfinallongserialVersionUID=6040103833179403725L;
  29. @Override
  30. protectedbooleanremoveEldestEntry(Entry<String,SoftReference<Bitmap>>eldest){
  31. if(size()>SOFT_CACHE_SIZE){
  32. returntrue;
  33. }
  34. returnfalse;
  35. }
  36. };
  37. }
  38. /**
  39. *从缓存中获取图片
  40. */
  41. publicBitmapgetBitmapFromCache(Stringurl){
  42. Bitmapbitmap;
  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. returnbitmap;
  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. returnbitmap;
  63. }else{
  64. mSoftCache.remove(url);
  65. }
  66. }
  67. }
  68. returnnull;
  69. }
  70. /**
  71. *添加图片到缓存
  72. */
  73. publicvoidaddBitmapToCache(Stringurl,Bitmapbitmap){
  74. if(bitmap!=null){
  75. synchronized(mLruCache){
  76. mLruCache.put(url,bitmap);
  77. }
  78. }
  79. }
  80. publicvoidclearCache(){
  81. mSoftCache.clear();
  82. }
  83. }
[java] view plain copy
  1. publicclassImageFileCache{
  2. privatestaticfinalStringCACHDIR="ImgCach";
  3. privatestaticfinalStringWHOLESALE_CONV=".cach";
  4. privatestaticfinalintMB=1024*1024;
  5. privatestaticfinalintCACHE_SIZE=10;
  6. privatestaticfinalintFREE_SD_SPACE_NEEDED_TO_CACHE=10;
  7. publicImageFileCache(){
  8. //清理文件缓存
  9. removeCache(getDirectory());
  10. }
  11. /**从缓存中获取图片**/
  12. publicBitmapgetImage(finalStringurl){
  13. finalStringpath=getDirectory()+"/"+convertUrlToFileName(url);
  14. Filefile=newFile(path);
  15. if(file.exists()){
  16. Bitmapbmp=BitmapFactory.decodeFile(path);
  17. if(bmp==null){
  18. file.delete();
  19. }else{
  20. updateFileTime(path);
  21. returnbmp;
  22. }
  23. }
  24. returnnull;
  25. }
  26. /**将图片存入文件缓存**/
  27. publicvoidsaveBitmap(Bitmapbm,Stringurl){
  28. if(bm==null){
  29. return;
  30. }
  31. //判断sdcard上的空间
  32. if(FREE_SD_SPACE_NEEDED_TO_CACHE>freeSpaceOnSd()){
  33. //SD空间不足
  34. return;
  35. }
  36. Stringfilename=convertUrlToFileName(url);
  37. Stringdir=getDirectory();
  38. FiledirFile=newFile(dir);
  39. if(!dirFile.exists())
  40. dirFile.mkdirs();
  41. Filefile=newFile(dir+"/"+filename);
  42. try{
  43. file.createNewFile();
  44. OutputStreamoutStream=newFileOutputStream(file);
  45. bm.compress(Bitmap.CompressFormat.JPEG,100,outStream);
  46. outStream.flush();
  47. outStream.close();
  48. }catch(FileNotFoundExceptione){
  49. Log.w("ImageFileCache","FileNotFoundException");
  50. }catch(IOExceptione){
  51. Log.w("ImageFileCache","IOException");
  52. }
  53. }
  54. /**
  55. *计算存储目录下的文件大小,
  56. *当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
  57. *那么删除40%最近没有被使用的文件
  58. */
  59. privatebooleanremoveCache(StringdirPath){
  60. Filedir=newFile(dirPath);
  61. File[]files=dir.listFiles();
  62. if(files==null){
  63. returntrue;
  64. }
  65. if(!android.os.Environment.getExternalStorageState().equals(
  66. android.os.Environment.MEDIA_MOUNTED)){
  67. returnfalse;
  68. }
  69. intdirSize=0;
  70. for(inti=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. intremoveFactor=(int)((0.4*files.length)+1);
  77. Arrays.sort(files,newFileLastModifSort());
  78. for(inti=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. returnfalse;
  86. }
  87. returntrue;
  88. }
  89. /**修改文件的最后修改时间**/
  90. publicvoidupdateFileTime(Stringpath){
  91. Filefile=newFile(path);
  92. longnewModifiedTime=System.currentTimeMillis();
  93. file.setLastModified(newModifiedTime);
  94. }
  95. /**计算sdcard上的剩余空间**/
  96. privateintfreeSpaceOnSd(){
  97. StatFsstat=newStatFs(Environment.getExternalStorageDirectory().getPath());
  98. doublesdFreeMB=((double)stat.getAvailableBlocks()*(double)stat.getBlockSize())/MB;
  99. return(int)sdFreeMB;
  100. }
  101. /**将url转成文件名**/
  102. privateStringconvertUrlToFileName(Stringurl){
  103. String[]strs=url.split("/");
  104. returnstrs[strs.length-1]+WHOLESALE_CONV;
  105. }
  106. /**获得缓存目录**/
  107. privateStringgetDirectory(){
  108. Stringdir=getSDPath()+"/"+CACHDIR;
  109. returndir;
  110. }
  111. /**取SD卡路径**/
  112. privateStringgetSDPath(){
  113. FilesdDir=null;
  114. booleansdCardExist=Environment.getExternalStorageState().equals(
  115. android.os.Environment.MEDIA_MOUNTED);//判断sd卡是否存在
  116. if(sdCardExist){
  117. sdDir=Environment.getExternalStorageDirectory();//获取根目录
  118. }
  119. if(sdDir!=null){
  120. returnsdDir.toString();
  121. }else{
  122. return"";
  123. }
  124. }
  125. /**
  126. *根据文件的最后修改时间进行排序
  127. */
  128. privateclassFileLastModifSortimplementsComparator<File>{
  129. publicintcompare(Filearg0,Filearg1){
  130. if(arg0.lastModified()>arg1.lastModified()){
  131. return1;
  132. }elseif(arg0.lastModified()==arg1.lastModified()){
  133. return0;
  134. }else{
  135. return-1;
  136. }
  137. }
  138. }
  139. }


网络下载图片

[java] view plain copy
  1. publicclassImageGetFromHttp{
  2. privatestaticfinalStringLOG_TAG="ImageGetFromHttp";
  3. publicstaticBitmapdownloadBitmap(Stringurl){
  4. finalHttpClientclient=newDefaultHttpClient();
  5. finalHttpGetgetRequest=newHttpGet(url);
  6. try{
  7. HttpResponseresponse=client.execute(getRequest);
  8. finalintstatusCode=response.getStatusLine().getStatusCode();
  9. if(statusCode!=HttpStatus.SC_OK){
  10. Log.w(LOG_TAG,"Error"+statusCode+"whileretrievingbitmapfrom"+url);
  11. returnnull;
  12. }
  13. finalHttpEntityentity=response.getEntity();
  14. if(entity!=null){
  15. InputStreaminputStream=null;
  16. try{
  17. inputStream=entity.getContent();
  18. FilterInputStreamfit=newFlushedInputStream(inputStream);
  19. returnBitmapFactory.decodeStream(fit);
  20. }finally{
  21. if(inputStream!=null){
  22. inputStream.close();
  23. inputStream=null;
  24. }
  25. entity.consumeContent();
  26. }
  27. }
  28. }catch(IOExceptione){
  29. getRequest.abort();
  30. Log.w(LOG_TAG,"I/Oerrorwhileretrievingbitmapfrom"+url,e);
  31. }catch(IllegalStateExceptione){
  32. getRequest.abort();
  33. Log.w(LOG_TAG,"IncorrectURL:"+url);
  34. }catch(Exceptione){
  35. getRequest.abort();
  36. Log.w(LOG_TAG,"Errorwhileretrievingbitmapfrom"+url,e);
  37. }finally{
  38. client.getConnectionManager().shutdown();
  39. }
  40. returnnull;
  41. }
  42. /*
  43. *AnInputStreamthatskipstheexactnumberofbytesprovided,unlessitreachesEOF.
  44. */
  45. staticclassFlushedInputStreamextendsFilterInputStream{
  46. publicFlushedInputStream(InputStreaminputStream){
  47. super(inputStream);
  48. }
  49. @Override
  50. publiclongskip(longn)throwsIOException{
  51. longtotalBytesSkipped=0L;
  52. while(totalBytesSkipped<n){
  53. longbytesSkipped=in.skip(n-totalBytesSkipped);
  54. if(bytesSkipped==0L){
  55. intb=read();
  56. if(b<0){
  57. break;//wereachedEOF
  58. }else{
  59. bytesSkipped=1;//wereadonebyte
  60. }
  61. }
  62. totalBytesSkipped+=bytesSkipped;
  63. }
  64. returntotalBytesSkipped;
  65. }
  66. }
  67. }


权限

[html] view plain copy
    1. <uses-permissionandroid:name="android.permission.INTERNET"/>
    2. <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

更多相关文章

  1. Android(安卓)ApiDemos示例解析(88):Media->VideoView
  2. Android热修复三部曲之动态加载补丁.dex文件
  3. android之ION内存管理器(1)-- 简介
  4. 探索Android该Parcel机制上
  5. 探索Android该Parcel机制(上)
  6. Android异步加载图像(含线程池,缓存方法)
  7. Android学习之Service(下)
  8. android 内存清理
  9. Android(安卓)SDK更新后 ADT R17 E/AndroidRuntime : java.lang.

随机推荐

  1. Android回弹阻尼效果的简单实现,非基于Li
  2. Android zipalign: error while loading
  3. Android 混合了 JSON 的 Android 应用程
  4. Android简单调用相机Camera功能,实现打开
  5. Android 源代码编后的目录分析
  6. 被裁员!大龄Android程序员面试惨败辛酸史,
  7. Android 动画详解
  8. Android之TextUtils类介绍
  9. 学习android
  10. 【Android】从无到有:手把手一步步教你使