在UI线程外处理Bitmap

使用AsyncTask
AsyncTask提供了一个方便的方法,你可以在一个后台线程中进行某些工作 ,然后把结果展现在UI线程中。

为了正确的使用AsyncTask类,以下是几条必须遵守的准则:

      1) Task的实例必须在UI 线程中创建

      2) execute方法必须在UI 线程中调用

      3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法,需要在UI线程中实例化这个task来调用。

      4) 该task只能被执行一次,否则多次调用时将会出现异常


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);
           
}
       
}
   
}
}
ImageView的 WeakReference引用确保了这个AsyncTask不会阻碍ImageView以及它所关联的任何引用的垃圾回收。不保证在task完成的时候ImageView仍然是存在的(没有被回收),所以必须要在onPostExecute()方法中检查是否为空。ImageView可能不会一直存在,例如,如果用户离开了当前的Activiy,或者在task完成前屏幕发送了旋转等。为了能够异步的开始加载bitmap,简单的创建一个先的task并开始执行:
public void loadBitmap(int resId, ImageView imageView) {
   
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
    task
.execute(resId);
}
处理并发
像ListView和GridView这种常用的View当使用AsyncTask的时候会存在另一个问题。为了更加有效的使用内存,这些组件会在用户滑动的时候回收子view。如果每个子view都启用了一个AsyncTask,就不能保证当这个task完成的时候,它对应的子view没有被回收,并且在用在了另一个子view中。而且也不能保证,开始这些task的顺序就是这些task完成的顺序。
关于上述问题会在Multithreading for Performance一文中进行介绍。并提供一个解决方法:ImageView存储了最近执行的一个AsyncTask的实例引用,这个AsyncTask可以在稍后被检查什么时候执行完成。
创建一个专用的Drawable的子类来存储task的一个引用。这样,
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()方法检查了是否有另一个运行中的task关联了这个ImageView。如果是,它会尝试调用cancle()方法取消前面的task:
public static boolean cancelPotentialWork(int data, ImageView imageView) {
   
final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

   
if (bitmapWorkerTask != null) {
       
final int bitmapData = bitmapWorkerTask.data;
       
// If bitmapData is not yet set or it differs from the new data
       
if (bitmapData == 0 || 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相关的task:
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;
}
最后一步就是在onPostExecute()方法中进行更新:
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这种控件中了。只要简单的调用loadBitmap方法就行了。例如,有一个GridView应该在它的Adapter的getView()方法中使用loadBitmap()方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值