android之listview缓存图片(缓存优化)

简介: 网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决。下面提出一些优化: 1、采用线程池 2、内存缓存+文件缓存 3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4 4、对下载的图片进行按比例缩放,以减少内存的消耗 具体的代码里面说明。先放上内存缓存类的代码MemoryCache.java: [java

网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决。下面提出一些优化:

1、采用线程池

2、内存缓存+文件缓存

3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的1/4

4、对下载的图片进行按比例缩放,以减少内存的消耗

具体的代码里面说明。先放上内存缓存类的代码MemoryCache.java:

  1. public class MemoryCache {  
  2.   
  3.     private static final String TAG = "MemoryCache";  
  4.     // 放入缓存时是个同步操作  
  5.     // LinkedHashMap构造方法的最后一个参数true代表这个map里的元素将按照最近使用次数由少到多排列,即LRU  
  6.     // 这样的好处是如果要将缓存中的元素替换,则先遍历出最近最少使用的元素来替换以提高效率  
  7.     private Map<String, Bitmap> cache = Collections  
  8.             .synchronizedMap(new LinkedHashMap<String, Bitmap>(101.5f, true));  
  9.     // 缓存中图片所占用的字节,初始0,将通过此变量严格控制缓存所占用的堆内存  
  10.     private long size = 0;// current allocated size  
  11.     // 缓存只能占用的最大堆内存  
  12.     private long limit = 1000000;// max memory in bytes  
  13.   
  14.     public MemoryCache() {  
  15.         // use 25% of available heap size  
  16.         setLimit(Runtime.getRuntime().maxMemory() / 4);  
  17.     }  
  18.   
  19.     public void setLimit(long new_limit) {   
  20.         limit = new_limit;  
  21.         Log.i(TAG, "MemoryCache will use up to " + limit / 1024. / 1024. + "MB");  
  22.     }  
  23.   
  24.     public Bitmap get(String id) {  
  25.         try {  
  26.             if (!cache.containsKey(id))  
  27.                 return null;  
  28.             return cache.get(id);  
  29.         } catch (NullPointerException ex) {  
  30.             return null;  
  31.         }  
  32.     }  
  33.   
  34.     public void put(String id, Bitmap bitmap) {  
  35.         try {  
  36.             if (cache.containsKey(id))  
  37.                 size -= getSizeInBytes(cache.get(id));  
  38.             cache.put(id, bitmap);  
  39.             size += getSizeInBytes(bitmap);  
  40.             checkSize();  
  41.         } catch (Throwable th) {  
  42.             th.printStackTrace();  
  43.         }  
  44.     }  
  45.   
  46.     /** 
  47.      * 严格控制堆内存,如果超过将首先替换最近最少使用的那个图片缓存 
  48.      *  
  49.      */  
  50.     private void checkSize() {  
  51.         Log.i(TAG, "cache size=" + size + " length=" + cache.size());  
  52.         if (size > limit) {  
  53.             // 先遍历最近最少使用的元素  
  54.             Iterator<Entry<String, Bitmap>> iter = cache.entrySet().iterator();  
  55.             while (iter.hasNext()) {  
  56.                 Entry<String, Bitmap> entry = iter.next();  
  57.                 size -= getSizeInBytes(entry.getValue());  
  58.                 iter.remove();  
  59.                 if (size <= limit)  
  60.                     break;  
  61.             }  
  62.             Log.i(TAG, "Clean cache. New size " + cache.size());  
  63.         }  
  64.     }  
  65.   
  66.     public void clear() {  
  67.         cache.clear();  
  68.     }  
  69.   
  70.     /** 
  71.      * 图片占用的内存 
  72.      *  
  73.      * @param bitmap 
  74.      * @return 
  75.      */  
  76.     long getSizeInBytes(Bitmap bitmap) {  
  77.         if (bitmap == null)  
  78.             return 0;  
  79.         return bitmap.getRowBytes() * bitmap.getHeight();  
  80.     }  
  81. }  

也可以使用SoftReference,代码会简单很多,但是我推荐上面的方法。

  1. public class MemoryCache {  
  2.       
  3.     private Map<String, SoftReference<Bitmap>> cache = Collections  
  4.             .synchronizedMap(new HashMap<String, SoftReference<Bitmap>>());  
  5.   
  6.     public Bitmap get(String id) {  
  7.         if (!cache.containsKey(id))  
  8.             return null;  
  9.         SoftReference<Bitmap> ref = cache.get(id);  
  10.         return ref.get();  
  11.     }  
  12.   
  13.     public void put(String id, Bitmap bitmap) {  
  14.         cache.put(id, new SoftReference<Bitmap>(bitmap));  
  15.     }  
  16.   
  17.     public void clear() {  
  18.         cache.clear();  
  19.     }  
  20.   
  21. }  
下面是文件缓存类的代码FileCache.java:
  1. public class FileCache {  
  2.   
  3.     private File cacheDir;  
  4.   
  5.     public FileCache(Context context) {  
  6.         // 如果有SD卡则在SD卡中建一个LazyList的目录存放缓存的图片  
  7.         // 没有SD卡就放在系统的缓存目录中  
  8.         if (android.os.Environment.getExternalStorageState().equals(  
  9.                 android.os.Environment.MEDIA_MOUNTED))  
  10.             cacheDir = new File(  
  11.                     android.os.Environment.getExternalStorageDirectory(),  
  12.                     "LazyList");  
  13.         else  
  14.             cacheDir = context.getCacheDir();  
  15.         if (!cacheDir.exists())  
  16.             cacheDir.mkdirs();  
  17.     }  
  18.   
  19.     public File getFile(String url) {  
  20.         // 将url的hashCode作为缓存的文件名  
  21.         String filename = String.valueOf(url.hashCode());  
  22.         // Another possible solution  
  23.         // String filename = URLEncoder.encode(url);  
  24.         File f = new File(cacheDir, filename);  
  25.         return f;  
  26.   
  27.     }  
  28.   
  29.     public void clear() {  
  30.         File[] files = cacheDir.listFiles();  
  31.         if (files == null)  
  32.             return;  
  33.         for (File f : files)  
  34.             f.delete();  
  35.     }  
  36.   
  37. }  
最后最重要的加载图片的类,ImageLoader.java:
  1. public class ImageLoader {  
  2.   
  3.     MemoryCache memoryCache = new MemoryCache();  
  4.     FileCache fileCache;  
  5.     private Map<ImageView, String> imageViews = Collections  
  6.             .synchronizedMap(new WeakHashMap<ImageView, String>());  
  7.     // 线程池  
  8.     ExecutorService executorService;  
  9.   
  10.     public ImageLoader(Context context) {  
  11.         fileCache = new FileCache(context);  
  12.         executorService = Executors.newFixedThreadPool(5);  
  13.     }  
  14.   
  15.     // 当进入listview时默认的图片,可换成你自己的默认图片  
  16.     final int stub_id = R.drawable.stub;  
  17.   
  18.     // 最主要的方法  
  19.     public void DisplayImage(String url, ImageView imageView) {  
  20.         imageViews.put(imageView, url);  
  21.         // 先从内存缓存中查找  
  22.   
  23.         Bitmap bitmap = memoryCache.get(url);  
  24.         if (bitmap != null)  
  25.             imageView.setImageBitmap(bitmap);  
  26.         else {  
  27.             // 若没有的话则开启新线程加载图片  
  28.             queuePhoto(url, imageView);  
  29.             imageView.setImageResource(stub_id);  
  30.         }  
  31.     }  
  32.   
  33.     private void queuePhoto(String url, ImageView imageView) {  
  34.         PhotoToLoad p = new PhotoToLoad(url, imageView);  
  35.         executorService.submit(new PhotosLoader(p));  
  36.     }  
  37.   
  38.     private Bitmap getBitmap(String url) {  
  39.         File f = fileCache.getFile(url);  
  40.   
  41.         // 先从文件缓存中查找是否有  
  42.         Bitmap b = decodeFile(f);  
  43.         if (b != null)  
  44.             return b;  
  45.   
  46.         // 最后从指定的url中下载图片  
  47.         try {  
  48.             Bitmap bitmap = null;  
  49.             URL imageUrl = new URL(url);  
  50.             HttpURLConnection conn = (HttpURLConnection) imageUrl  
  51.                     .openConnection();  
  52.             conn.setConnectTimeout(30000);  
  53.             conn.setReadTimeout(30000);  
  54.             conn.setInstanceFollowRedirects(true);  
  55.             InputStream is = conn.getInputStream();  
  56.             OutputStream os = new FileOutputStream(f);  
  57.             CopyStream(is, os);  
  58.             os.close();  
  59.             bitmap = decodeFile(f);  
  60.             return bitmap;  
  61.         } catch (Exception ex) {  
  62.             ex.printStackTrace();  
  63.             return null;  
  64.         }  
  65.     }  
  66.   
  67.     // decode这个图片并且按比例缩放以减少内存消耗,虚拟机对每张图片的缓存大小也是有限制的  
  68.     private Bitmap decodeFile(File f) {  
  69.         try {  
  70.             // decode image size  
  71.             BitmapFactory.Options o = new BitmapFactory.Options();  
  72.             o.inJustDecodeBounds = true;  
  73.             BitmapFactory.decodeStream(new FileInputStream(f), null, o);  
  74.   
  75.             // Find the correct scale value. It should be the power of 2.  
  76.             final int REQUIRED_SIZE = 70;  
  77.             int width_tmp = o.outWidth, height_tmp = o.outHeight;  
  78.             int scale = 1;  
  79.             while (true) {  
  80.                 if (width_tmp / 2 < REQUIRED_SIZE  
  81.                         || height_tmp / 2 < REQUIRED_SIZE)  
  82.                     break;  
  83.                 width_tmp /= 2;  
  84.                 height_tmp /= 2;  
  85.                 scale *= 2;  
  86.             }  
  87.   
  88.             // decode with inSampleSize  
  89.             BitmapFactory.Options o2 = new BitmapFactory.Options();  
  90.             o2.inSampleSize = scale;  
  91.             return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);  
  92.         } catch (FileNotFoundException e) {  
  93.         }  
  94.         return null;  
  95.     }  
  96.   
  97.     // Task for the queue  
  98.     private class PhotoToLoad {  
  99.         public String url;  
  100.         public ImageView imageView;  
  101.   
  102.         public PhotoToLoad(String u, ImageView i) {  
  103.             url = u;  
  104.             imageView = i;  
  105.         }  
  106.     }  
  107.   
  108.     class PhotosLoader implements Runnable {  
  109.         PhotoToLoad photoToLoad;  
  110.   
  111.         PhotosLoader(PhotoToLoad photoToLoad) {  
  112.             this.photoToLoad = photoToLoad;  
  113.         }  
  114.   
  115.         @Override  
  116.         public void run() {  
  117.             if (imageViewReused(photoToLoad))  
  118.                 return;  
  119.             Bitmap bmp = getBitmap(photoToLoad.url);  
  120.             memoryCache.put(photoToLoad.url, bmp);  
  121.             if (imageViewReused(photoToLoad))  
  122.                 return;  
  123.             BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);  
  124.             // 更新的操作放在UI线程中  
  125.             Activity a = (Activity) photoToLoad.imageView.getContext();  
  126.             a.runOnUiThread(bd);  
  127.         }  
  128.     }  
  129.   
  130.     /** 
  131.      * 防止图片错位 
  132.      *  
  133.      * @param photoToLoad 
  134.      * @return 
  135.      */  
  136.     boolean imageViewReused(PhotoToLoad photoToLoad) {  
  137.         String tag = imageViews.get(photoToLoad.imageView);  
  138.         if (tag == null || !tag.equals(photoToLoad.url))  
  139.             return true;  
  140.         return false;  
  141.     }  
  142.   
  143.     // 用于在UI线程中更新界面  
  144.     class BitmapDisplayer implements Runnable {  
  145.         Bitmap bitmap;  
  146.         PhotoToLoad photoToLoad;  
  147.   
  148.         public BitmapDisplayer(Bitmap b, PhotoToLoad p) {  
  149.             bitmap = b;  
  150.             photoToLoad = p;  
  151.         }  
  152.   
  153.         public void run() {  
  154.             if (imageViewReused(photoToLoad))  
  155.                 return;  
  156.             if (bitmap != null)  
  157.                 photoToLoad.imageView.setImageBitmap(bitmap);  
  158.             else  
  159.                 photoToLoad.imageView.setImageResource(stub_id);  
  160.         }  
  161.     }  
  162.   
  163.     public void clearCache() {  
  164.         memoryCache.clear();  
  165.         fileCache.clear();  
  166.     }  
  167.   
  168.     public static void CopyStream(InputStream is, OutputStream os) {  
  169.         final int buffer_size = 1024;  
  170.         try {  
  171.             byte[] bytes = new byte[buffer_size];  
  172.             for (;;) {  
  173.                 int count = is.read(bytes, 0, buffer_size);  
  174.                 if (count == -1)  
  175.                     break;  
  176.                 os.write(bytes, 0, count);  
  177.             }  
  178.         } catch (Exception ex) {  
  179.         }  
  180.     }  
  181. }  
主要流程是先从内存缓存中查找,若没有再开线程,从文件缓存中查找都没有则从指定的url中查找,并对bitmap进行处理,最后通过下面方法对UI进行更新操作。
  1. a.runOnUiThread(...);  

在你的程序中的基本用法:

  1. ImageLoader imageLoader=new ImageLoader(context);  
  2. ...  
  3. imageLoader.DisplayImage(url, imageView);  
比如你的放在你的ListView的adapter的getView()方法中,当然也适用于GridView。OK,先到这。
目录
相关文章
|
28天前
|
缓存 监控 前端开发
在资源加载优化中,如何利用浏览器缓存提升性能?
通过以上这些方法,可以有效地利用浏览器缓存来提升资源加载的性能,减少网络请求次数,提高用户体验和应用的响应速度。同时,需要根据具体的应用场景和资源特点进行灵活调整和优化,以达到最佳的效果。此外,随着技术的不断发展和变化,还需要持续关注和学习新的缓存优化方法和策略。
89 53
|
2月前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
80 15
Android 系统缓存扫描与清理方法分析
|
1月前
|
缓存 监控 测试技术
如何利用浏览器的缓存来优化网站性能?
【10月更文挑战第23天】通过以上多种方法合理利用浏览器缓存,可以显著提高网站的性能,减少网络请求,加快资源加载速度,提升用户的访问体验。同时,要根据网站的具体情况和资源的特点,不断优化和调整缓存策略,以适应不断变化的业务需求和用户访问模式。
93 7
|
3月前
|
存储 缓存 编解码
Android经典面试题之图片Bitmap怎么做优化
本文介绍了图片相关的内存优化方法,包括分辨率适配、图片压缩与缓存。文中详细讲解了如何根据不同分辨率放置图片资源,避免图片拉伸变形;并通过示例代码展示了使用`BitmapFactory.Options`进行图片压缩的具体步骤。此外,还介绍了Glide等第三方库如何利用LRU算法实现高效图片缓存。
72 20
Android经典面试题之图片Bitmap怎么做优化
|
2月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
64 4
|
1月前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。
|
2月前
|
缓存 JavaScript 前端开发
Vue 3的事件监听缓存如何优化性能?
【10月更文挑战第5天】随着前端应用复杂度的增加,性能优化变得至关重要。Vue 3 通过引入事件监听缓存等新特性提升了应用性能。本文通过具体示例介绍这一特性,解释其工作原理及如何利用它优化性能。与 Vue 2 相比,Vue 3 可在首次渲染时注册事件监听器并在后续渲染时重用,避免重复注册导致的资源浪费和潜在内存泄漏问题。通过使用 `watchEffect` 或 `watch` 监听状态变化并更新监听器,进一步提升应用性能。事件监听缓存有助于减少浏览器负担,特别在大型应用中效果显著,使应用更加流畅和响应迅速。
94 1
|
3月前
|
缓存 JavaScript 中间件
优化Express.js应用程序性能:缓存策略、请求压缩和路由匹配
在开发Express.js应用时,采用合理的缓存策略、请求压缩及优化路由匹配可大幅提升性能。本文介绍如何利用`express.static`实现缓存、`compression`中间件压缩响应数据,并通过精确匹配、模块化路由及参数化路由提高路由处理效率,从而打造高效应用。
192 14
|
2月前
|
存储 缓存 监控
HTTP:强缓存优化实践
HTTP强缓存是提升网站性能的关键技术之一。通过精心设计缓存策略,不仅可以显著减少网络延迟,还能降低服务器负载,提升用户体验。实施上述最佳实践,结合持续的监控与调整,能够确保缓存机制高效且稳定地服务于网站性能优化目标。
56 3
|
3月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
77 8