在應用中經常需要下載很多的圖片,因此,寫好圖片下載部分的代碼非常關鍵。不好的代碼很容易建立太多的對象,導致經常執行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://www.bkjia.com/uploadfile/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;
}
作者“程式人生”