图片加载AsyncTask并发问题
在列表控件中使用AsycnTask加载图片时,会带来并发问题。
如果每个子视图都触发一个AsyncTask,因为AsyncTask内部是一个线程池,并发触发时,不能确保每个子视图的AsyncTask都进入了队列,而且异步任务的完成顺序和启动顺序也不一定一致。
Multithreading For Performance这篇文章提供了一种方法。
主要方案如下:
-
通过定制一个BitmapDrawable,让ImageView储存当前AsyncTask的引用(弱引用)
BitmapLoadTask,一个加载图片的AsyncTask,可以执行从网络、文件加载图片
class AsyncDrawable extends BitmapDrawable {
private final WeakReference<DownloadTask> downloadTaskWeakReference;
AsyncDrawable(BitmapLoadTask downloadTask) {
this.downloadTaskWeakReference = new WeakReference<>(downloadTask);
}
public DownloadTask getDownloadTask() {
return downloadTaskWeakReference.get();
}
}
-
ListView显示一个ImageView,并开始下载之前,判断是否有另一个AsyncTask已经与该ImageView绑定
- 如果存在一个Task,并且它的任务就是当前ImageView的任务,则不会新建一个AsyncTask去下载
- 如果不存在,或者存在的任务执行的下载不同于当前的任务,就取消当期的Task,然后新建一个。
下载时判断:
public void loadBitmap(String url, ImageView imageView) {
if (shouldNewTaskToLoad(url, imageView)) {
final BitmapLoadTask bitmapLoadTask = new BitmapLoadTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(bitmapLoadTask);
imageView.setImageDrawable(asyncDrawable);
bitmapLoadTask.execute(url);
}
}
```java
判断的逻辑:
```java
public static boolean shouldNewTaskToLoad(String url, ImageView imageView) {
if (imageView != null) {
AsyncDrawable asyncDrawable = (AsyncDrawable) imageView.getDrawable();
if (asyncDrawable != null) {
BitmapLoadTask bitmapLoadTask = asyncDrawable.getDownloadTask();
if (bitmapLoadTask != null) {
//如果当前要下载的图片的地址与ImageView中储存的Task下载的地址不想等
if (url == null || (!url.equals(bitmapLoadTask.url))) {
bitmapLoadTask.cancel(true);
} else {
return false;
}
}
}
}
return true;
}
- 使用:在加载图片时:使用
setImageDrawable将AsyncTask与ImageView关联
if (shouldNewTaskToLoad(url, imageView)) {
final BitmapLoadTask bitmapLoadTask = new BitmapLoadTask(imageView);
final AsyncDrawable asyncDrawable = new AsyncDrawable(bitmapLoadTask);
imageView.setImageDrawable(asyncDrawable);
bitmapLoadTask.execute(url);
}