首页 > 程序开发 > 移动开发 > Android >

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

2011-09-19

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

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

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


Java代码
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.callback != 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.callback.update(bitmap, item.imageUrl);
}
};

public static class ImageDownloadItem {
public String imageUrl;//需要下载的图片URL
public String imagePath;//下载的后图片路径
public ImageDownloadCallback callback;//回调方法
}

public static interface ImageDownloadCallback {
//策略模式,由子类实现
public void update(Bitmap bitmap, String imageUrl);
}
}

下面是使用的代码片段

Java代码
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://up.2cto.com/2011/0919/20110919010659183.jpg";
imageView.setTag(imageUrl);

//设置下载项
ImageDownloadItem item = new ImageDownloadItem();
item.imageUrl = imageUrl;
//如果是无需显示图片的情况(如预下载),无需设置item.callback,即让item.callback = null
item.callback = new ImageDownloadCallback() {
@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;
}

作者“程序人生”

相关文章
最新文章
热点推荐