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

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

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

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,先到这。
目录
相关文章
|
26天前
|
缓存 监控 Java
构建高效Android应用:从优化用户体验到提升性能
在竞争激烈的移动应用市场中,为用户提供流畅和高效的体验是至关重要的。本文深入探讨了如何通过多种技术手段来优化Android应用的性能,包括UI响应性、内存管理和多线程处理。同时,我们还将讨论如何利用最新的Android框架和工具来诊断和解决性能瓶颈。通过实例分析和最佳实践,读者将能够理解并实施必要的优化策略,以确保他们的应用在保持响应迅速的同时,还能够有效地利用系统资源。
|
1月前
|
调度 数据库 Android开发
构建高效Android应用:Kotlin协程的实践与优化
在Android开发领域,Kotlin以其简洁的语法和平台友好性成为了开发的首选语言。其中,Kotlin协程作为处理异步任务的强大工具,它通过提供轻量级的线程管理机制,使得开发者能够在不阻塞主线程的情况下执行后台任务,从而提升应用性能和用户体验。本文将深入探讨Kotlin协程的核心概念,并通过实例演示如何在实际的Android应用中有效地使用协程进行网络请求、数据库操作以及UI的流畅更新。同时,我们还将讨论协程的调试技巧和常见问题的解决方法,以帮助开发者避免常见的陷阱,构建更加健壮和高效的Android应用。
36 4
|
1月前
|
数据库 Android开发 开发者
构建高效Android应用:采用Kotlin协程优化网络请求处理
【2月更文挑战第30天】 在移动应用开发领域,网络请求的处理是影响用户体验的关键环节。针对Android平台,利用Kotlin协程能够极大提升异步任务处理的效率和简洁性。本文将探讨如何通过Kotlin协程优化Android应用中的网络请求处理流程,包括协程的基本概念、网络请求的异步执行以及错误处理等方面,旨在帮助开发者构建更加流畅和响应迅速的Android应用。
|
1月前
|
API 数据库 Android开发
构建高效Android应用:探究Kotlin多线程优化策略
【2月更文挑战第14天】随着移动设备性能的日益强大,用户对应用程序的响应速度和流畅性要求越来越高。在Android开发中,合理利用多线程技术是提升应用性能的关键手段之一。Kotlin作为一种现代的编程语言,其协程特性为开发者提供了更为简洁高效的多线程处理方式。本文将深入探讨使用Kotlin进行Android多线程编程的最佳实践,包括协程的基本概念、优势以及在实际项目中的应用场景和性能优化技巧,旨在帮助开发者构建更加高效稳定的Android应用。
|
2月前
|
搜索推荐 安全 Android开发
如何优化安卓应用的用户体验
【2月更文挑战第9天】在当今移动互联网时代,安卓应用已成为人们日常生活中不可或缺的一部分。然而,用户对应用的使用体验越来越苛刻,一个不好的应用体验很容易导致用户的流失。本文将介绍如何从多个方面优化安卓应用的用户体验。
|
6月前
|
JSON JavaScript 前端开发
Android 缩减、混淆处理和优化应用研究(二)
Android 缩减、混淆处理和优化应用研究(二)
|
23天前
|
Java Android开发 开发者
构建高效Android应用:Kotlin协程的实践与优化
在响应式编程范式日益盛行的今天,Kotlin协程作为一种轻量级的线程管理解决方案,为Android开发带来了性能和效率的双重提升。本文旨在探讨Kotlin协程的核心概念、实践方法及其在Android应用中的优化策略,帮助开发者构建更加流畅和高效的应用程序。通过深入分析协程的原理与应用场景,结合实际案例,本文将指导读者如何优雅地解决异步任务处理,避免阻塞UI线程,从而优化用户体验。
|
1天前
|
移动开发 Java Android开发
构建高效Android应用:采用Kotlin协程优化网络请求
【4月更文挑战第24天】 在移动开发领域,尤其是对于Android平台而言,网络请求是一个不可或缺的功能。然而,随着用户对应用响应速度和稳定性要求的不断提高,传统的异步处理方式如回调地狱和RxJava已逐渐显示出局限性。本文将探讨如何利用Kotlin协程来简化异步代码,提升网络请求的效率和可读性。我们将深入分析协程的原理,并通过一个实际案例展示如何在Android应用中集成和优化网络请求。
|
7天前
|
缓存 移动开发 Android开发
构建高效Android应用:从优化用户体验到提升性能表现
【4月更文挑战第18天】 在移动开发的世界中,打造一个既快速又流畅的Android应用并非易事。本文深入探讨了如何通过一系列创新的技术策略来提升应用性能和用户体验。我们将从用户界面(UI)设计的简约性原则出发,探索响应式布局和Material Design的实践,再深入剖析后台任务处理、内存管理和电池寿命优化的技巧。此外,文中还将讨论最新的Android Jetpack组件如何帮助开发者更高效地构建高质量的应用。此内容不仅适合经验丰富的开发者深化理解,也适合初学者构建起对Android高效开发的基础认识。
5 0
|
13天前
|
存储 数据库 Android开发
构建高效安卓应用:采用Jetpack架构组件优化用户体验
【4月更文挑战第12天】 在当今快速发展的数字时代,Android 应用程序的流畅性与响应速度对用户满意度至关重要。为提高应用性能并降低维护成本,开发者需寻求先进的技术解决方案。本文将探讨如何利用 Android Jetpack 中的架构组件 — 如 LiveData、ViewModel 和 Room — 来构建高质量的安卓应用。通过具体实施案例分析,我们将展示这些组件如何协同工作以实现数据持久化、界面与逻辑分离,以及确保数据的即时更新,从而优化用户体验并提升应用的可维护性和可测试性。