更有效率的载入图片【2】

使用AsyncTask类

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()方法里。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值