`

高效率下载图片——防止内存溢出

 
阅读更多

在应用中经常需要下载很多的图片,因此,写好图片下载部分的代码非常关键。不好的代码很容易创建太多的对象,导致经常执行GC,接着就出现了ANR;也很容易导致内存溢出OOM。

 

现在,我从防止ANR和OOM的角度写下载图片的代码。再来分析一下需求,当我需要为图片列表下载很多张图片时,我期望图片是有顺序地一张一张显示,而不是开启很多线程同时下载多张图片(注意:这样也会影响每个线程的执行速度)。

 

 

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;

public class ImageDownloadThread extends Thread {
	//单例类
	private ImageDownloadThread() {}
	private static ImageDownloadThread imageDownloadThread = null;
	public static ImageDownloadThread getInstance() {
		if (imageDownloadThread == null) {
			imageDownloadThread = new ImageDownloadThread();
			imageDownloadThread.start();//创建后立刻运行
		}
		return imageDownloadThread;
	}
	
	//缓存下载图片
	private Map<String, String> cache = new HashMap<String, String>();//KEY:图片URL;VALUE:下载后的图片路径
	public boolean isDownload(String imageUrl) {
		return cache.containsKey(imageUrl);
	}
	public Bitmap downloadWithCache(ImageDownloadItem item) {
		if (cache.containsKey(item.imageUrl)) {
			Bitmap bitmap = BitmapFactory.decodeFile(cache.get(item.imageUrl));
			return bitmap;
		} else {
			addDownloadItem(item);
		}
		return null;
	}
	public void downloadWithoutCache(ImageDownloadItem item) {
		addDownloadItem(item);
	}

	//下载队列
	private List<ImageDownloadItem> queue = new ArrayList<ImageDownloadItem>();
	private synchronized void addDownloadItem(ImageDownloadItem item) {
		queue.add(item);
		this.notify();//添加了下载项就激活本线程
	}

	@Override
	public void run() {
		while(true) {
			while(queue.size() > 0) {
				ImageDownloadItem item = queue.remove(0);
				String imagePath = downloadImage(item.imageUrl);
				//缓存图片路径
				cache.put(item.imageUrl, imagePath);

				if (item.downloadListener != null) {//需要执行回调来显示图片
					item.imagePath = imagePath;

					//交由UI线程处理
					Message msg = handler.obtainMessage();
					msg.obj = item;
					handler.sendMessage(msg);
				}
			}
			try {
				synchronized(this) {
					this.wait();//没有下载项时等待
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	private String downloadImage(String imageUrl) {
		//TODO
		//不提供该方法代码
		//下载部分应该有专门下载文件的类(如:FileDownloadUtil.download(imageUrl))
		return "";
	}

	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			ImageDownloadItem item = (ImageDownloadItem)msg.obj;
			Bitmap bitmap = BitmapFactory.decodeFile(item.imagePath);
			item.downloadListener.update(bitmap, item.imageUrl);
		}
	};

	public static class ImageDownloadItem {
		public String imageUrl;//需要下载的图片URL
		public String imagePath;//下载的后图片路径
		private PostDownloadListener downloadListener;//回调监听
		public void setPostDownloadListener(PostDownloadListener listener) {
			this.downloadListener = listener;
		}
	}
	
	public static interface PostDownloadListener {
		//策略模式,由子类实现
		public void update(Bitmap bitmap, String imageUrl);
	}
}

 

下面是使用的代码片段

 

public View getView(int position, View convertView, ViewGroup vg) {
		final ImageView imageView;
		if (convertView != null) {
			imageView = (ImageView)convertView;
		} else {
			imageView = new ImageView(this);
		}
		//在实际应用中imageUrl值是不同的
		String imageUrl = "http://www.nxnet.net/yule/yljj/200710/W020071008388975463611.jpg";
		imageView.setTag(imageUrl);
		
		//设置下载项
		ImageDownloadItem item = new ImageDownloadItem();
		item.imageUrl = imageUrl;
		//如果是无需显示图片的情况(如预下载),无需设置PostDownloadListener监听器
		item.setPostDownloadListener(new PostDownloadListener() {
			@Override
			public void update(Bitmap bitmap, String imageUrl) {
				ImageView imageViewByTag = (ImageView)imageView.findViewWithTag(imageUrl);
				if (imageViewByTag != null) imageViewByTag.setImageBitmap(bitmap);
			}
		});
		
		ImageDownloadThread imageDownloadThread = ImageDownloadThread.getInstance();
		Bitmap bitmap = imageDownloadThread.downloadWithCache(item);
		if (bitmap != null) {//从缓存中取到
			imageView.setImageBitmap(bitmap);
		}
		return imageView;
	}
0
1
分享到:
评论

相关推荐

    安卓Android源码——加载本地图片,绝对不会出现OOM.zip

    在安卓应用开发中,加载本地图片是一个常见的任务,但如果不恰当处理,很容易导致内存溢出(Out Of Memory,简称OOM)问题。这个压缩包“安卓Android源码——加载本地图片,绝对不会出现OOM.zip”显然是提供了避免...

    LazyList——对ListView加载网络图片的优化,解决各种问题

    然而,当ListView中的每个列表项都包含网络图片时,可能会遇到性能问题,如卡顿、内存溢出(OOM)等。`LazyList`就是为了解决这些问题而设计的一种解决方案。 `LazyList`的核心思想是“延迟加载”(Lazy Loading)...

    安卓Android源码——安卓Android 图片缓存、加载器.rar

    Android的Bitmap类提供了解码和缩放图片的功能,避免了加载过大图片导致内存溢出的问题。 5. **异步加载**:为避免UI线程阻塞,图片加载通常在后台线程执行,加载完成后通过Handler或者RxJava等工具将结果传递回...

    Linux 内存管理导读 .pdf

    由于CPU速度远超存储器,为了弥补速度差,引入了存储层次,利用局部性原理——程序倾向于重复访问最近或附近的内存位置,来优化性能。 Linux的目标在于: 1. **减小footprint**:提升缓存命中率,充分利用局部性...

    Android——仿美图秀秀和IOS系统的相机胶卷(实用1).zip

    为解决这个问题,我们可以采用` Glide`、`Picasso`或`Fresco`这样的第三方库,它们优化了图片加载过程,支持内存和磁盘缓存,能够有效防止内存溢出。对于图片编辑功能,如裁剪、滤镜等,可以利用`Bitmap`对象进行...

    内存设计资料.rar

    9. **内存安全**:防止缓冲区溢出、指针异常等内存相关安全问题,是软件开发的重要环节。C++的智能指针、 Rust 的所有权模型等都是为了解决这些问题。 10. **内存扩展**:随着技术的发展,内存的容量也在不断扩展。...

    C语言——彩票管理系统.zip

    - **内存管理**:C语言没有自动垃圾回收,因此需要手动分配和释放内存,防止内存泄漏。 - **数据安全**:由于直接操作内存,要防止缓冲区溢出和指针错误,确保数据的安全性。 - **代码可读性**:使用合适的命名,...

    1SC16C2552B——带16字节FIFO的双UART器件

    - **16字节FIFO缓冲区**:SC16C2552B配备了16字节的发送和接收先进先出(FIFO)缓冲区,这一设计极大地提高了数据处理效率,减少了处理器的负担,尤其在高数据流情况下,可以有效防止数据溢出和丢失。 - **调制解调...

    关系型数据库应用的性能优化——基于SQL Server平台.pdf

    数据库缓存是内存的主要消费者,设置合理的内存上限要考虑工作线程、数据库连接和其他应用的需求,防止溢出到Pagefile。通过性能监视器,我们应确保Paging使用为零,并关注SQL Server Buffer Manager的Average Page ...

    MemoryAnalyzer-1.7.0.20170613-win32.win32.x86

    总之,Memory Analyzer作为一款强大的JVM内存泄漏分析工具,对于优化Java应用的性能、防止内存溢出等问题,具有不可替代的作用。掌握MAT的使用方法,可以让我们更好地驾驭Java内存管理,提升应用的稳定性和效率。

    操作系统(内存管理)

    (映射是一个表示一一对应关系的数学术语 —— 当内存的虚拟地址有一个对应的物理地址来存储内存内容时,该内存将被映射。) 基于 UNIX 的系统有两个可映射到附加内存中的基本系统调用: brk: brk() 是一个非常...

    bmp格式图片缩小

    - **内存管理**:处理大尺寸图像时,要小心内存分配,避免内存溢出。 - **文件操作**:正确处理文件读写,防止文件被其他进程占用或损坏。 优化方面,可以考虑以下策略: - **算法优化**:采用更高效的插值算法,...

    C语言知识列表 ——》C专家

    - **高效性**:C语言编写的程序通常执行效率高,因为它们接近于机器代码。 - **简洁性**:C语言语法简洁,关键字数量较少,易于学习和使用。 #### 三、C语言的关键概念 1. **变量声明与类型**: - C语言支持多种...

    (转)大数据量的excel文件读取——2003及之前版本(含代码及示例)

    在读取大量数据时,使用POI可以避免内存溢出,因为它支持流式处理。 2. **SXSSF与HSSF的区别**:HSSF是POI中处理.BIFF格式的API,适用于读取和写入老版本的Excel文件。而SXSSF是用于处理大文件的内存效率更高的API...

    Adobe Reader's Custom Memory Management :a Heap of trouble.pdf

    因此,未来的研究方向可能需要探索更加高效且安全的内存管理方案,既能够满足高性能的需求,又能有效防止各种形式的安全攻击。 综上所述,Adobe Reader的自定义内存管理系统虽然提升了软件的整体性能,但也引入了...

    java poi 读取百万数据OOM优化

    Excel文件通常存储为二进制格式,如`.xls`或`.xlsx`,这些文件可以容纳大量的行和列,但当数据量过大时,可能会引发内存溢出(Out Of Memory, OOM)错误。在这种情况下,我们需要对代码进行优化以避免此类问题。本篇...

    【荐】mysql服务性能优化—my.cnf_my.ini配置说明详解(16G内存)

    - **影响**:过小会影响大事务的执行效率,过大则可能引发内存溢出问题。 - **sort_buffer_size=2M**: - **作用**:每个连接执行排序操作时分配的缓冲区大小。 - **影响**:合理的设置能够提高排序效率,但过大...

    《密码编码学——加密方法的C与C++实现》

    AES则已成为目前广泛使用的对称加密标准,具有较高的安全性和效率。 接着,书中的内容可能延伸到非对称加密,如RSA和椭圆曲线加密(ECC)。RSA是一种公钥密码体制,由一对公钥和私钥组成,适合于大量数据的加密。...

Global site tag (gtag.js) - Google Analytics