public void loadBitmap(int resId, ImageView imageView) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(resId);
}
处理并发问题
像
ListView和
GridView 这种常见的视图组件,结合
AsyncTask
一起使用的时候会产生另外一个问题。为了尽可能的提高内存的使用效率,当用户滚动组件时,这类视图组件会循环利用它的子视图,如果每个子视图都触发一个
AsyncTask ,那么将不会有保证相关联的视图是否已经回收供另外一个子视图循环使用了。而且,异步任务的执行顺序并不意味着任务的执行结束时的顺序也是这样的。结合这两种原因,引发的这个并发问题有可能会导致图片设置到子视图上时会发生错位。
//存储着一个与之对应的task, 在任务还在执行时充当一个占位符,当任务完成时,随之被替换
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap,
BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference =
new WeakReference(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
public void loadBitmap(int resId, ImageView imageView) {
if (cancelPotentialWork(resId, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
// mPlaceHolderBitmap 引用的就是默认要显示的图片对象
final AsyncDrawable asyncDrawable =
new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
cancelPotentialWork
方法是用来检测与
ImageView
相关联的另外一个任务是否正在执行,如果是的话,将会通过调用
cancel()
方法来
关闭这个 任务。只有极少部分的情况下两个任务的图片数据会相等,如果相等的话,那也不会出现什么并发问题了,我们也不需要做任何的处理
public static boolean cancelPotentialWork(int data, ImageView imageView) {
// 取得当前与ImageView相关联的任务
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
// 如果存在的话,则取消
if (bitmapWorkerTask != null) {
final int bitmapData = bitmapWorkerTask.data;
if (bitmapData != data) {
// Cancel previous task
bitmapWorkerTask.cancel(true);
} else {
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was cancelled
return true;
}
下面是
getBitmapWorkerTask()
方法的定义
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
// imageView有可能被回收
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
现在在
onPostExecute()
方法中我们就需要检测任务是否被取消了,还有需要检测下当前的任务是否是与imageView相关联的任务
class BitmapWorkerTask extends AsyncTask {
...
@Override
protected void onPostExecute(Bitmap bitmap) {
// if task cancelled , this method is never invoked
// why check here ?
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask =
getBitmapWorkerTask(imageView);
// 判断当前task是否是相关联的task
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}