使用AsyncTask类
AsyncTask并不妨碍目标ImageView和任何引用被系统垃圾收集。所以你还必须检查参考onPostExecute()来确认 ImageView的可能不再存在。
要开始异步加载位图,只需创建一个新的任务,并执行它:
并发处理
一些常见的视图组件如ListView和GridView在与AsyncTask的配合使用时会引发另一个问题。为了能够高效的内存,这些组件会在当用户滚动时回收子视图,如果每个子视图触发AsyncTask的,有没有保证,当它完成时,相关的视图已经被回收。此外,异步任务开始的顺序是不能保证他们完成的顺序。
在执行BitmapWorkerTask之前,先创建了一个AsyncDrawable,并将其绑定到目标的ImageView:
引用cancelPotentialWork方法检查上面的代码示例,如果其他正在运行的任务和ImageView建立联系,则它试图通过调用cancel()取消了之前的任务。在少数情况下,新的任务数据与现有的任务匹配,没有什么需要发生。以下代码演示了cancelPotentialWork:
最后一步是在BitmapWorkerTask更新onPostExecute(),以便检查是否任务被取消还是与ImageView的当前的任务相匹配:
这种实现方法可以适用于ListView和GridView这类会回收子视图的组件,当你设置imageview图像时只需简单的调用loadBitmap。例如,在一个GridView的实现中,这将存在在适配器的getView()方法里。
AsyncTask类提供了一种简单的方法,在后台线程执行一些工作并在UI线程上公布结果。
要使用它,创建一个子类,并覆盖所提供的方法。这里是使用AsyncTask的decodeSampledBitmapFromResource()将一个大的图像加载到一个ImageView的的一个例子:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private int data = 0;
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
data = params[0];
return decodeSampledBitmapFromResource(getResources(), data, 100, 100));
}
// Once complete, see if ImageView is still around and set bitmap.
@Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
AsyncTask并不妨碍目标ImageView和任何引用被系统垃圾收集。所以你还必须检查参考onPostExecute()来确认 ImageView的可能不再存在。
要开始异步加载位图,只需创建一个新的任务,并执行它:
public void loadBitmap(int resId, ImageView imageView) {
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(resId);
}
并发处理
一些常见的视图组件如ListView和GridView在与AsyncTask的配合使用时会引发另一个问题。为了能够高效的内存,这些组件会在当用户滚动时回收子视图,如果每个子视图触发AsyncTask的,有没有保证,当它完成时,相关的视图已经被回收。此外,异步任务开始的顺序是不能保证他们完成的顺序。
创建一个专用的Drawable的子类存储一个工作任务。在这种情况下,使用一个BitmapDrawable占位符可以使后台任务完成任务时imageview得以显示图像。
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap,
BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference =
new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}
在执行BitmapWorkerTask之前,先创建了一个AsyncDrawable,并将其绑定到目标的ImageView:
public void loadBitmap(int resId, ImageView imageView) {
if (cancelPotentialWork(resId, imageView)) {
final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
final AsyncDrawable asyncDrawable =
new AsyncDrawable(getResources(), mPlaceHolderBitmap, task);
imageView.setImageDrawable(asyncDrawable);
task.execute(resId);
}
}
引用cancelPotentialWork方法检查上面的代码示例,如果其他正在运行的任务和ImageView建立联系,则它试图通过调用cancel()取消了之前的任务。在少数情况下,新的任务数据与现有的任务匹配,没有什么需要发生。以下代码演示了cancelPotentialWork:
public static boolean cancelPotentialWork(int data, 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(),用来获取特定的ImageView的相关任务:
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getBitmapWorkerTask();
}
}
return null;
}
最后一步是在BitmapWorkerTask更新onPostExecute(),以便检查是否任务被取消还是与ImageView的当前的任务相匹配:
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
final BitmapWorkerTask bitmapWorkerTask =
getBitmapWorkerTask(imageView);
if (this == bitmapWorkerTask && imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
这种实现方法可以适用于ListView和GridView这类会回收子视图的组件,当你设置imageview图像时只需简单的调用loadBitmap。例如,在一个GridView的实现中,这将存在在适配器的getView()方法里。