Android 获取远程图片与本地图片缓存

标签: Android图片缓存   3438人阅读  评论(0)  收藏  举报   分类: Android App(30) 

[java]  view plain  copy  print ?
  1. "font-family: 'Microsoft YaHei';">1.意义:加快读取速度,减少流量的消耗,减少崩溃的次数  

2.Android应用中的UI现成5秒没有相应的话就会强制抛出异常,俗称ANR(Appliction Not Responce),对于获取远程的资源,这里特指的是从服务器获取的数据譬如图片等等,这种异常将会更加容易被抛出来,所以在android 4.0 里面将限制了网络的访问,不允许将网络的访问放在主线程,低于4.0的版本就不会收到限制,这个是在测试的时候发现的,Android中提供两个方法来做这件事情:

启动一个心的现成来获取资源,完成后通过handler机制发送消息,同时在handler的接收端更新主线程,从而达到异步线程获取图片,接收端定义handler变量,同事复写handlMessage(Message msg)方法

本地缓存

 对图片来说,你不可能让应用每次获取的时候都重新到远程服务器去下载,特别是显示ListView中的图片的时候,滑动的速度变得很快,这样将会造成ANR,即使图片比较小,但是图片还没来得及释放的话,累计的图片将会占用比较大的内存,但是又不能将所有的图片资源在获取之后放在内存中,使用弱引用保存对象的方法保存,因为图片的资源往往很占内存也比较容易造成ANR,那么如果下载下来的图片保存的SdCard中,下次直接从SDcard上去获取的话,是比较靠谱的缓存方法,采用LRU等一些算法可以保证sdcard被占用的空间的一小部分,这样即保证了图片的加载,节省了从远程获取的图片流量,又使Sdcard的空间只占用了一笑部分,另外一中方法是设置LRU规则跟过期的时间

代码的流程如下:

下载图片--->判断Sdcard上的空间--->判断开辟的10Mde空间--->保存图片--->过期策略

2.1在Sdcard上开辟一定的空间,需要先判断Sdcard上剩余的空间是否足够,如果足够的话,就可以开辟空间,例如开辟10M的内存空间用来保存图片

2.2当需要获取图片的时候,就先从Sdcard上的目录中去找,如果找的到的话,使用该图片,并且更新图片最后被使用的时间,如果找不到,通过URL去DownLoad

2.3去服务器下载图片,如果图片下载成功了,放入SDcard,并使用,如果失败了,应该有重试的机制重新下载,譬如三次

2.4下载成功后保存到Sdcard上需要判断10M的空间是否已经用完,如果没用完就保存,如果已经用完空间,就根据LRU规则删除一些最近没有被用户用到的资源

保存图片到SD的代码:

[java]  view plain  copy  print ?
  1. private void saveBmpToSd(Bitmap bm, String url) {  
  2.         if (bm == null) {  
  3.             Log.w(TAG, " trying to savenull bitmap");  
  4.             return;  
  5.         }  
  6.         // 判断sdcard上的空间  
  7.         if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {  
  8.             Log.w(TAG, "Low free space onsd, do not cache");  
  9.             return;  
  10.         }  
  11.         String filename = convertUrlToFileName(url);  
  12.         String dir = getDirectory(filename);  
  13.         File file = new File(dir + "/" + filename);  
  14.         try {  
  15.             file.createNewFile();  
  16.             OutputStream outStream = new FileOutputStream(file);  
  17.             bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);  
  18.             outStream.flush();  
  19.             outStream.close();  
  20.             Log.i(TAG, "Image saved tosd");  
  21.         } catch (FileNotFoundException e) {  
  22.             Log.w(TAG, "FileNotFoundException");  
  23.         } catch (IOException e) {  
  24.             Log.w(TAG, "IOException");  
  25.         }  
  26.     }  

计算Sdcard上的空间:

[java]  view plain  copy  print ?
  1. /** 
  2.      * 计算sdcard上的剩余空间 
  3.      *  
  4.      * @return 
  5.      */  
  6.     private int freeSpaceOnSd() {  
  7.         // TODO Auto-generated method stub  
  8.         StatFs stat = new StatFs(Environment.getExternalStorageDirectory()  
  9.                 .getPath());  
  10.         double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat  
  11.                 .getBlockSize()); // MB  
  12.         return (int) sdFreeMB;  
  13.     }  


修改文件的最后修改时间:

[java]  view plain  copy  print ?
  1. /** 
  2.      * 修改文件的最后修改时间 
  3.      *  
  4.      * @param dir 
  5.      * @param fileName 
  6.      */  
  7.     private void updateFileTime(String dir, String fileName) {  
  8.         File file = new File(dir, fileName);  
  9.         long newModifiedTime = System.currentTimeMillis();  
  10.         file.setLastModified(newModifiedTime);  
  11.     }  


本地缓存优化:

[java]  view plain  copy  print ?
  1. /** 
  2.      * 计算储存目录下的文件大小,当文件的总大小超过规定的CACHE_SIZE, 
  3.      * 或者是Sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE 的规定 ,那么删除40%最近没有使用的图片 
  4.      *  
  5.      * @param dirPath 
  6.      */  
  7.     private void removeCache(String dirPath) {  
  8.         File file = new File(dirPath);  
  9.         File[] files = file.listFiles();  
  10.         if (files == null) {  
  11.             return;  
  12.         }  
  13.         int dirSize = 0;  
  14.         for (int i = 0; i < files.length; i++) {  
  15.             if (files[i].getName().contains(WHOLESALE_CONV)) {  
  16.                 dirSize += files[i].length();  
  17.             }  
  18.         }  
  19.         if (dirSize > CACHE_SIZE * MB  
  20.                 || FREE_SD_SPACE_NEEED_TO_CACHE > freeSpaceOnSd()) {  
  21.             int removeFactor = (int) ((0.4 * files.length) + 10);  
  22.             Arrays.sort(files, new FileLastModifSort());  
  23.             Log.i(TAG, "Clear some expiredcache files");  
  24.             for (int i = 0; i < removeFactor; i++) {  
  25.                 if (files[i].getName().contains(WHOLESALE_CONV)) {  
  26.                     files[i].delete();  
  27.                 }  
  28.             }  
  29.         }  
  30.     }  
[java]  view plain  copy  print ?
  1. /** 
  2.      * 删除过期文件 
  3.      * @param dirPath 
  4.      * @param fileName 
  5.      */  
  6.     private void removeExpiredCache(String dirPath, String fileName) {  
  7.         File file = new File(dirPath, fileName);  
  8.         if (System.currentTimeMillis() - file.lastModified() > M_TIME_DIFF) {  
  9.             Log.i(TAG, "Clear some expiredcache files");  
  10.             file.delete();  
  11.         }  
  12.     }  


文件使用时间排序:

[java]  view plain  copy  print ?
  1. /** 
  2.      * 根据文件的最后修改时间进行排序 
  3.      * @author huanglong 
  4.      * 
  5.      */  
  6.     class FileLastModifSort implements Comparator {  
  7.         public int compare(File arg0, File arg1) {  
  8.             if (arg0.lastModified() > arg1.lastModified()) {  
  9.                 return 1;  
  10.             } else if (arg0.lastModified() == arg1.lastModified()) {  
  11.                 return 0;  
  12.             } else {  
  13.                 return -1;  
  14.             }  
  15.         }  
  16.     }  

内存保存:

在内存中保存的话,只能保存一定的量,而不能一直往里面放,需要设置数据的过期时间,LRU等算法,这里有一个方法是把常用的数据放到一个缓存中(A),不常用的放在另外一个缓存中(B),当要获取数据时候先从A中去获取,如果A中存在在去B中获取,B中的数据主要是A中LUR出来的数据,这里的内存回收主要是针对B,从而保持A中的数据可以有效的被命中。

定义A缓存:

[java]  view plain  copy  print ?
  1. // 定义A缓存  
  2.     private final HashMap mHardBItmapCache = new LinkedHashMap(  
  3.             HARD_CACHE_CAPACITY / 20.75f, true) {  
  4.         @Override  
  5.         protected boolean removeEldestEntry(  
  6.                 java.util.Map.Entry eldest) {  
  7.             // TODO Auto-generated method stub  
  8.             if (size() > HARD_CACHE_CAPACITY) {  
  9.                 // 保证map的size大于30时候,把最不常用的key放到mSoftBitmapCache中,从而保证mHardBitmapCache的效率  
  10.                 mSoftBitmapCache.put(eldest.getKey(),  
  11.                         new SoftReference(eldest.getValue()));  
  12.                 return true;  
  13.             } else {  
  14.                 return false;  
  15.             }  
  16.         }  
  17.     };  

定义B缓存:

[java]  view plain  copy  print ?
  1. // 定义B缓存  
  2.     // 当mHardBitmapCache的key大于30的时候,会根据LRU算法把最近没有使用的Key放入到缓存中  
  3.     // Bitmap 使用了SoftReference 当内存不足的时候,此时cache中的bitmap会被垃圾回收掉  
  4.     private final static ConcurrentHashMap> mSoftBitmapCache = new ConcurrentHashMap>(  
  5.             HARD_CACHE_CAPACITY / 2);  

从缓存中获取数据:

[java]  view plain  copy  print ?
  1. /** 
  2.      * 从缓存中获得数据 
  3.      *  
  4.      * @param url 
  5.      * @return 
  6.      */  
  7.     private Bitmap getBitmapFromeCache(String url) {  
  8.         // 先从mHardBitmapCache缓存中获取  
  9.         synchronized (mHardBItmapCache) {  
  10.             final Bitmap bitmap = mHardBItmapCache.get(url);  
  11.             if (bitmap != null) {  
  12.                 // 如果找到的话,把元素移动到linkedHashMap的最前面,从而保证LRU算法中是最后被删除的  
  13.                 mHardBItmapCache.remove(url);  
  14.                 mHardBItmapCache.put(url, bitmap);  
  15.                 return bitmap;  
  16.             }  
  17.         }  
  18.         // 如果mHardBitmapCache中找不到,到mSoftBitmapCache中找  
  19.         SoftReference bitmapReference = mSoftBitmapCache.get(url);  
  20.         if (bitmapReference != null) {  
  21.             final Bitmap bitmap = bitmapReference.get();  
  22.             if (bitmap != null) {  
  23.                 return bitmap;  
  24.             } else {  
  25.                 mSoftBitmapCache.remove(url);  
  26.             }  
  27.         }  
  28.         return null;  
  29.     }  

如果缓存不存在,那么就只能去服务器下载:

[java]  view plain  copy  print ?
  1. // 如果缓存中不存在那么就只能去服务器下载  
  2.     class ImageDownloaderTask extends AsyncTask {  
  3.         private static final int IO_BUFFER_SIZE = 4 * 1024;  
  4.         private String url;  
  5.         private final WeakReference imageViewReferrReference;  
  6.   
  7.         private ImageDownloaderTask() {  
  8.             imageViewReferrReference = new WeakReference(imageView);  
  9.   
  10.         }  
  11.   
  12.         @Override  
  13.         protected Bitmap doInBackground(String... params) {  
  14.             // TODO Auto-generated method stub  
  15.             final AndroidHttpClient client = AndroidHttpClient  
  16.                     .newInstance("Android");  
  17.             url = params[0];  
  18.             final HttpGet getRequest = new HttpGet(url);  
  19.             try {  
  20.                 HttpResponse response = client.execute(getRequest);  
  21.                 final int statusCode = response.getStatusLine().getStatusCode();  
  22.                 if (statusCode != HttpStatus.SC_OK) {  
  23.                     Log.v(TAG, "从" + url + "中下载图片是出错!  错误码:" + statusCode);  
  24.                     return null;  
  25.                 }  
  26.                 final HttpEntity entity = response.getEntity();  
  27.                 if (entity != null) {  
  28.                     InputStream inputStream = null;  
  29.                     OutputStream outputStream = null;  
  30.                     try {  
  31.                         inputStream = entity.getContent();  
  32.                         final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();  
  33.                         outputStream = new BufferedOutputStream(dataStream,  
  34.                                 IO_BUFFER_SIZE);  
  35.                         copy(inputStream, outputStream);  
  36.                         outputStream.flush();  
  37.                         final byte[] data = dataStream.toByteArray();  
  38.                         final Bitmap bitma = BitmapFactory.decodeByteArray(  
  39.                                 data, 0, data.length);  
  40.                     } finally {  
  41.                         if (inputStream != null) {  
  42.                             inputStream.close();  
  43.                         }  
  44.                         if (outputStream != null) {  
  45.                             outputStream.close();  
  46.                         }  
  47.                         entity.consumeContent();  
  48.                     }  
  49.                 }  
  50.             } catch (IOException e) {  
  51.                 // TODO Auto-generated catch block  
  52.                 getRequest.abort();  
  53.                 Log.w(TAG, "Incorrect URL :" + url);  
  54.                 e.printStackTrace();  
  55.             } finally {  
  56.                 if (client != null) {  
  57.                     client.close();  
  58.                 }  
  59.             }  
  60.   
  61.             return null;  
  62.         }  
  63.     }  

这是两种做法,还有一些应用在下载的时候使用了线程池和消息队列MQ,对于图片的下载效果会更好一些


http://mobile.51cto.com/android-288600.htm

更多相关文章

  1. android 异步获取图片
  2. Android之——清理手机SD卡缓存
  3. Android大图片内存清理
  4. Android 如何在关于手机界面添加个图片
  5. android Lru图片缓存管理方案
  6. Android 中动态提取图片中颜色作为主题色
  7. ListView有背景图片或背景颜色,那么在滑动ListView的时候,ListView
  8. Android 获取并显示远程图片 Picasso框架的使用(二)

随机推荐

  1. android BitMap回收
  2. Android AES加密解密
  3. android从资源文件中读取文件流显示
  4. android studio的Gradle各种配置汇总
  5. Android(安卓)破解apk文件
  6. 第四周
  7. android数据单位dp,px和sp
  8. wm命令使用方法(修改android 分辨率)修
  9. Android JNI学习笔记(五)-这两篇,足够了解jni
  10. Android tcpdump 抓包