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

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

你可能感兴趣的:(android)