`
heisedeyueya
  • 浏览: 97746 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类

ListView异步加载网络图片之双缓存技术

阅读更多
ListView异步加载网络图片完美版之双缓存技术

   本示例参考学习了一个国外的示例:http://code.google.com/p/android-imagedownloader/,有兴趣的同学下载研究一下。
问题描述:在这一篇博客中将会为大家讲解如何将下载回来的图片进行缓存,为了节约流量,并且提高下一次显示图片的速度,提高用户体验,所以不能够每次调用getView的时候都去从网络下载图片,就必须用到缓存。
缓存的重点问题:如何控制缓存的大小,如果我们一直向缓存中筛数据,而没有对缓存的大小进行控制,那么最终会导致OOM
解决方案:设置两级缓存,第一级用LinkedHashMap<String,Bitmap>保留Bitmap的强引用,但是控制缓存的大小MAX_CAPACITY=10,当继续向该缓存中存数据的时候,将会把一级缓存中的最近最少使用的元素放入二级缓存ConcurrentHashMap<String, SoftReference<Bitmap>>,二级缓存中保留的Bitmap的软引用。
SoftReference:它保存的对象实例,除非JVM即将OutOfMemory,否则不会被GC回收。这个特性使得它特别适合设计对象Cache。对于Cache,我们希望被缓存的对象最好始终常驻内存,但是如果JVM内存吃紧,为了不发生OutOfMemoryError导致系统崩溃,必要的时候也允许JVM回收Cache的内存,待后续合适的时机再把数据重新Load到Cache中。这样可以系统设计得更具弹性。

// 0.75是加载因子为经验值,true则表示按照最近访问量的高低排序,false则表示按照插入顺序排序
private HashMap<String, Bitmap> mFirstLevelCache = new LinkedHashMap<String, Bitmap>(
			MAX_CAPACITY / 2, 0.75f, true) {
		private static final long serialVersionUID = 1L;
		protected boolean removeEldestEntry(Entry<String, Bitmap> eldest) {
			if (size() > MAX_CAPACITY) {// 当超过一级缓存阈值的时候,将老的值从一级缓存搬到二级缓存
				mSecondLevelCache.put(eldest.getKey(),
						new SoftReference<Bitmap>(eldest.getValue()));
				return true;
			}
			return false;
		};
	};	

加载图片:先读缓存,缓存么有就开启异步任务从网络下载
/**
	 * 加载图片,如果缓存中有就直接从缓存中拿,缓存中没有就下载
	 * @param url
	 * @param adapter
	 * @param holder
	 */
public void loadImage(String url, BaseAdapter adapter, ViewHolder holder) {
		resetPurgeTimer();
		Bitmap bitmap = getBitmapFromCache(url);// 从缓存中读取
		if (bitmap == null) {
			holder.mImageView.setImageResource(R.drawable.ic_launcher);//缓存没有设为默认图片
			ImageLoadTask imageLoadTask = new ImageLoadTask();
			imageLoadTask.execute(url, adapter, holder);//执行异步任务
		} else {
			holder.mImageView.setImageBitmap(bitmap);//设为缓存图片
		}

	}
   
读取缓存的代码:
public Bitmap getBitmapFromCache(String url) {
		Bitmap bitmap = null;
		bitmap = getFromFirstLevelCache(url);// 从一级缓存中拿
		if (bitmap != null) {
			return bitmap;
		}
		bitmap = getFromSecondLevelCache(url);//从二级缓存中拿
		return bitmap;
	}
private Bitmap getFromFirstLevelCache(String url) {
		Bitmap bitmap = null;
		synchronized (mFirstLevelCache) {
			bitmap = mFirstLevelCache.get(url);
			if (bitmap != null) {// 将最近访问的元素放到链的头部,提高下一次访问该元素的检索速度(LRU算法)
				mFirstLevelCache.remove(url);
				mFirstLevelCache.put(url, bitmap);
			}
		}
		return bitmap;
	}
private Bitmap getFromSecondLevelCache(String url) {
		Bitmap bitmap = null;
		SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);
		if (softReference != null) {
			bitmap = softReference.get();
			if (bitmap == null) {// 由于内存吃紧,软引用已经被gc回收了
				mSecondLevelCache.remove(url);
			}
		}
		return bitmap;
	}

定期清理缓存
// 定时清理缓存
	private Runnable mClearCache = new Runnable() {
		@Override
		public void run() {
			clear();
		}
	};
	private Handler mPurgeHandler = new Handler();

	// 重置缓存清理的timer
	private void resetPurgeTimer() {
		mPurgeHandler.removeCallbacks(mClearCache);
		mPurgeHandler.postDelayed(mClearCache, DELAY_BEFORE_PURGE);
	}

	/**
	 * 清理缓存
	 */
	private void clear() {
		mFirstLevelCache.clear();
		mSecondLevelCache.clear();
	}

总结:这篇文章主要讲了图片的缓存技巧,拿来主义,学习从别人的代码中吸取精华,代码我也上传了,并且附有详细的注释,这里的缓存都是在内存当中,适合短期有效的缓存,如果是长期有效的图片,我们可以采用文件存储的方式,再设一级文件缓存,有兴趣的同学可以研究一下。感谢您耐心的看完了,送您一杯咖啡

  • 大小: 95.4 KB
分享到:
评论
7 楼 Kendra 2014-01-28  
  贊
6 楼 heisedeyueya 2012-11-28  
安轩之 写道
哥们,拿你的代码下载下来测试。

SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);
if (softReference != null) {
bitmap = softReference.get();
if (bitmap == null) {//
mSecondLevelCache.remove(url);
}
}
return bitmap;

softReference每次都是空,可想而知你的这个代码没有缓存了。

希望改进,嘿嘿。这个源码小弟受教了,还是非常感谢。希望楼主改改,希望下次测试以后发布。

确实代码还有很多需要改进的地方,空了改一下,多谢批评
5 楼 heisedeyueya 2012-11-28  
安轩之 写道
哥们,拿你的代码下载下来测试。

SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);
if (softReference != null) {
bitmap = softReference.get();
if (bitmap == null) {//
mSecondLevelCache.remove(url);
}
}
return bitmap;

softReference每次都是空,可想而知你的这个代码没有缓存了。

希望改进,嘿嘿。这个源码小弟受教了,还是非常感谢。希望楼主改改,希望下次测试以后发布。

这里为空是因为这个map中还没该url的图片缓存,所以接下来就回去晚上下载了
4 楼 heisedeyueya 2012-11-28  
安轩之 写道
不知道楼主测试了没有。如果不适用一级缓存,把一级缓存注释了,2级缓存也没有执行完,直接报错、

java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@4181f7d8 rejected from java.util.concurrent.ThreadPoolExecutor@414bddd0[Running, pool size = 128, active threads = 126, queued tasks = 10, completed tasks = 26]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1967)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:782)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1303)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:564)
at android.os.AsyncTask.execute(AsyncTask.java:511)
at isun.heisedeyueya.ImageLoader.loadImage(ImageLoader.java:139)
at isun.heisedeyueya.MyAdapter.getView(MyAdapter.java:116)
at android.widget.AbsListView.obtainView(AbsListView.java:2012)
at android.widget.ListView.makeAndAddView(ListView.java:1772)
at android.widget.ListView.fillDown(ListView.java:672)
at android.widget.ListView.fillSpecific(ListView.java:1330)
at android.widget.ListView.layoutChildren(ListView.java:1603)
at android.widget.AbsListView.onLayout(AbsListView.java:1863)

线程池溢出了。

如果我不使用一级缓存,用二级缓存来做,应该怎么改?请赐教。

Q:375290562
谢谢

如果我们一直向缓存中筛数据,而没有对缓存的大小进行控制,那么最终会导致OOM
安轩之 写道
哥们,拿你的代码下载下来测试。

SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);
if (softReference != null) {
bitmap = softReference.get();
if (bitmap == null) {//
mSecondLevelCache.remove(url);
}
}
return bitmap;

softReference每次都是空,可想而知你的这个代码没有缓存了。

希望改进,嘿嘿。这个源码小弟受教了,还是非常感谢。希望楼主改改,希望下次测试以后发布。

可以在外部控制线程的数量,应为AsynTask默认池子的大小是128。
3 楼 安轩之 2012-11-28  
不知道楼主测试了没有。如果不适用一级缓存,把一级缓存注释了,2级缓存也没有执行完,直接报错、

java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@4181f7d8 rejected from java.util.concurrent.ThreadPoolExecutor@414bddd0[Running, pool size = 128, active threads = 126, queued tasks = 10, completed tasks = 26]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1967)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:782)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1303)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:564)
at android.os.AsyncTask.execute(AsyncTask.java:511)
at isun.heisedeyueya.ImageLoader.loadImage(ImageLoader.java:139)
at isun.heisedeyueya.MyAdapter.getView(MyAdapter.java:116)
at android.widget.AbsListView.obtainView(AbsListView.java:2012)
at android.widget.ListView.makeAndAddView(ListView.java:1772)
at android.widget.ListView.fillDown(ListView.java:672)
at android.widget.ListView.fillSpecific(ListView.java:1330)
at android.widget.ListView.layoutChildren(ListView.java:1603)
at android.widget.AbsListView.onLayout(AbsListView.java:1863)

线程池溢出了。

如果我不使用一级缓存,用二级缓存来做,应该怎么改?请赐教。

Q:375290562
谢谢
2 楼 安轩之 2012-11-28  
哥们,哦哦,搞错了。一级缓存中已经处理了。看错了,不好意思 
1 楼 安轩之 2012-11-28  
哥们,拿你的代码下载下来测试。

SoftReference<Bitmap> softReference = mSecondLevelCache.get(url);
if (softReference != null) {
bitmap = softReference.get();
if (bitmap == null) {//
mSecondLevelCache.remove(url);
}
}
return bitmap;

softReference每次都是空,可想而知你的这个代码没有缓存了。

希望改进,嘿嘿。这个源码小弟受教了,还是非常感谢。希望楼主改改,希望下次测试以后发布。

相关推荐

    listview 异步加载网络图片

    10. **总结**:在Android的ListView中实现异步加载网络图片,需要结合异步处理框架、选择合适的图片库、优化缓存策略、合理管理内存,并对ListView进行优化。通过这些手段,可以显著提升应用的性能和用户体验。

    ListView异步加载网络图片

    总之,ListView异步加载网络图片是一项关键技术,它涉及到Android多线程、内存管理和图片缓存策略。合理地运用这些技术,可以构建出性能优良、用户体验良好的应用。同时,使用SoftReference可以平衡内存使用和图片...

    Android实现ListView异步加载图片

    "Android实现ListView异步加载图片" Android 实现 ListView 异步加载图片是一种常见的技术,旨在提高应用程序的性能和用户体验。本文将详细介绍 Android 中实现 ListView 异步加载图片的方法,并对相关的技术概念...

    ListView异步加载图片进度条

    综上所述,实现ListView中的异步加载图片进度条,需要综合运用多线程、图片缓存、UI更新等技术,并遵循良好的代码组织和错误处理原则。通过合理的封装和接口设计,可以使得代码更加规范,同时也提升了用户体验。

    listview异步加载网络图片

    总的来说,“listview异步加载图片”是一项关键的性能优化技术,它结合了异步编程和内存管理的策略,确保了ListView的流畅滚动和系统的稳定运行。通过理解并应用这些知识,开发者可以创建出更高效、更用户友好的...

    android listView 异步加载图片

    这里的“android listView 异步加载图片”指的是在不阻塞UI线程的情况下,从网络、本地存储或其他来源加载图片到ListView中的技术。 这篇名为“ImageLoader”的Java文件很可能就是一个实现图片异步加载的工具类。在...

    ListView的异步加载图片并缓存

    因此,"ListView的异步加载图片并缓存"是解决这一问题的关键技术。 异步加载图片的主要目的是将耗时的操作(如网络请求、图片解码)放在后台线程,以避免阻塞UI线程,提高用户体验。以下是一些关键知识点: 1. **...

    ListView异步加载图片三级缓存

    总结起来,"ListView异步加载图片三级缓存"是一个重要的Android性能优化技巧,它结合了异步处理、内存管理、磁盘操作以及网络通信,为用户提供流畅、高效的图片浏览体验。在LazyLoaderDemo这样的示例代码中,我们...

    实现ListView的异步加载,图片的本地缓存,以及手势识别

    为了提升用户体验,我们需要实现ListView的异步加载,图片的本地缓存,以及手势识别功能。 1. **异步加载**: 异步加载是指在后台线程中加载数据,避免阻塞主线程,从而保证UI的流畅性。对于ListView,我们可以...

    图片异步加载,照片墙,异步加载listview图片2

    在Android应用开发中,图片异步加载是一种常见的优化技术,特别是在构建类似照片墙或ListView这样的大量图片展示场景中。这个话题主要关注如何有效地处理图片资源,避免UI阻塞,提高用户体验。以下是对"图片异步加载...

    ListView异步加载网络大图Demo

    这篇教程"ListView异步加载网络大图Demo"主要讲解如何高效地在ListView中异步加载网络图片,以避免性能问题。 首先,我们需要理解Bitmap对象在Android中的内存管理。Bitmap是Android用来存储像素信息的类,占用大量...

    listview item 异步加载数据图片

    在Android开发中,ListView是一种常用的UI控件,用于展示大量可滚动的数据列表。然而,当ListView中的每个item都需要加载图片时,...通过分析这些代码,开发者可以更好地理解和应用ListView异步加载数据图片的技术。

    ListView异步加载并缓存图片

    总结来说,"ListView异步加载并缓存图片"是一个关键的性能优化技术,结合LruCache和软引用可以有效地管理内存,同时利用SD卡存储扩展缓存能力。在实际开发中,理解并运用这些技术能显著提升Android应用的性能和用户...

    Android中ListView全面完美的网络图片的异步加载

    综上所述,实现Android中ListView全面完美的网络图片异步加载需要结合异步加载库(如Picasso或Glide)、LruCache缓存策略以及动态加载技术。这些方法的运用可以显著提升应用性能,为用户提供流畅的滚动体验,同时...

    listview异步加载图片

    本文将详细探讨如何在ListView中实现异步加载网络图片,主要涉及的知识点包括异步处理、图片缓存策略以及AsyncImageLoader的使用。 首先,异步加载图片的基本思想是将耗时的网络请求和图片解码工作放在后台线程进行...

    Android初学者之Listview异步加载网络图片并动

    对于初学者来说,理解并实现ListView的异步加载网络图片以及动态更新是非常重要的技能。这个主题通常涵盖以下几个关键知识点: 1. **ListView的基础使用**:首先,你需要了解ListView的基本结构,包括Adapter、...

    listview异步加载网络数据

    本示例将围绕“ListView异步加载网络数据”这一主题展开,主要涉及`AsyncTask`的使用。 `AsyncTask`是Android提供的一种轻量级的异步处理类,适用于短时间、不频繁的后台任务,特别适合于进行网络请求、数据库操作...

    listview 异步加载图片

    综上所述,ListView异步加载图片是一个涉及多方面优化的技术,包括异步处理、缓存策略、图片压缩、第三方库的使用以及滚动优化等。正确实施这些策略,能够显著提升用户在浏览包含大量图片的ListView时的体验。

    官方 listview 异步加载图片

    "官方 listview 异步加载图片"这个主题就是关于如何在ListView中高效、平滑地加载图片的技术实践。 异步加载的基本原理是在主线程之外(通常在后台线程)加载图片,避免阻塞UI更新,从而保证界面的流畅性。在这个...

Global site tag (gtag.js) - Google Analytics