Bitmap 处理之不要在UI主线程中处理Bitmap

原文链接 http://developer.android.com/intl/zh-CN/training/displaying-bitmaps/process-bitmap.html

在使用BitmapFactory.decode*解析图片时,最好不要在UI主线程中处理,因为图片的来源是未知的,有可能是从硬盘读取的,也有可能是是网络的图片资源,这时在解析图片时,会有一些不可控的因素,如(网速较慢等),如果在UI主线中处理,就会有可能block主线程,从而导致应用无相应(ANR),会造成很不好的用户体验。Android本身提供很多的方法,在非UI线程中处理一些比较耗时的操作,如Handler,AsyncTask等,下面是使用AsyncTask的一种方法。

使用AsyncTask

AsyncTask提供了一种很简单的方式,在后台线程处理复杂的操作,处理完成之后,会将处理之后的结果返回到UI主线程,具体的实现入下:

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<ImageView> imageViewReference 使用一个软引用,保证ImageView可以被回收, 主要的耗时的操作在doInBackground(Integer... params) 方法中实施,处理完成之后的返回的结果在onPostExecute方法中使用,onPostExecute是在主线程运行的。 方法decodeSampledBitmapFromResource 的具体实现请参考 “Bitmap处理之流畅的加载大的Bitmap”
实际的应用:

 public void loadBitmap(int resId, ImageView imageView) {

   BitmapWorkerTask task = new BitmapWorkerTask(imageView);

   task.execute(resId);

 }

两个参数分别是 resId是通过R.drawable 获得的资源图片,imageView是 要显示的图片的ImageView

处理并发问题

对于普通的View如ListViwe和GridView在联合上述的AsyncTask加载资源时,会导致其他的问题。为了更好的利用内存资源,这些Viwe在滑动时,会对子View做资源回收,如果子View使用AsyncTask加载资源时,就不能保证子View能够及时的回收资源,也就是说开始一步加载的顺序和完成的顺序会不一致。 关于多线程处理的问题可以参考 “Multithreading for Performance ”的一篇Blog 可以创建一个专有的BitmapDrawable,在AsyncTask加载图片完成之前,可以使用一个empty(预览图)bitmap来显示

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 执行之前,可以先使用一个预览图显示(或者一张空的图片)

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);

   }

 }

mPlaceHolderBitmap可以是一个empty_photo.png的图片
cancelPotentialWork 方法是检查是否有另外一个running task和ImageView绑定,如果是,则前一个task会执行cancel()

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;

}

如果返回false,说明已经有一个工作者线程在运行 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类

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);

           }

       }

   }

}

加入了判断条件,判断当前的线程是否已经被cancel,ImageView的线程是否和当前的线程是否是同一个线程


应用场景
如在ListView或者GridView的getView()方法中,可以点用loadBitmap()方法来加载子view的资源
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值