处理来自UI线程的位图

移动开发 Android
本文通过具体方法讲解了如何处理来自UI线程的位图,希望读完本文对各位读者朋友有一定的帮助和启发。

BitmapFactory的decode()方法,在Load Large Bitmaps Efficiently要 点中进行讨论,不应该执行在主UI线程如果要读取源数据从磁盘或网络位置(或相对内存来说任何别的真实来源).该数据需要加载的时间是不可预知的,并取决 于多种因素(从磁盘或网络的读取速度,图像大小,CPU的功率,等).如果这些任务阻塞UI线程,系统标志您的应用程序无响应,用户可以选择关闭它响应 (有关更多信息,请参阅Designing for Responsiveness).

本文将引导您通过在后台线程中使用AsyncTask处理位图,并告诉您如何处理并发问题.

使用一个异步任务

AsyncTask类提供了一种简单的方式来在一个后台线程中执行许多任务,并且把结果反馈给UI线程.使用的方法是,创建一个继承与它的子类并且实现提供的方法.这里是一个使用AsyncTask和decodeSampledBitmapFromResource()加载一个大图片到ImageView中的例子:

  1. class BitmapWorkerTask extends AsyncTask { 
  2.     private final WeakReference imageViewReference; 
  3.     private int data = 0
  4.  
  5.     public BitmapWorkerTask(ImageView imageView) { 
  6.         // Use a WeakReference to ensure the ImageView can be garbage collected 
  7.         imageViewReference = new WeakReference(imageView); 
  8.     } 
  9.  
  10.     // Decode image in background. 
  11.     @Override 
  12.     protected Bitmap doInBackground(Integer... params) { 
  13.         data = params[0]; 
  14.         return decodeSampledBitmapFromResource(getResources(), data, 100100)); 
  15.     } 
  16.  
  17.     // Once complete, see if ImageView is still around and set bitmap. 
  18.     @Override 
  19.     protected void onPostExecute(Bitmap bitmap) { 
  20.         if (imageViewReference null) { 
  21.             final ImageView imageView = imageViewReference.get(); 
  22.             if (imageView != null) { 
  23.                 imageView.setImageBitmap(bitmap); 
  24.             } 
  25.         } 
  26.     } 
  27.  } 

对于ImageView来说WeakReference确保那时AsyncTask并不会阻碍ImageView和任何它的引用被垃圾回收期回收.不能保证ImageView在任务完成后仍然存在,所以你必须在onPostExecute()方法中检查它的引用.ImageView可能不再存在,如果例如,如果在任务完成之前用户退出了活动或者配置发生了变化.

为了异步地加载位图,简单地创建一个新的任务并且执行它:

  1. public void loadBitmap(int resId, ImageView imageView) { 
  2.     BitmapWorkerTask task = new BitmapWorkerTask(imageView); 
  3.     task.execute(resId); 
  4.  } 

处理并发

常见的视图组件例如ListView和GridView如在上一节中当和AsyncTask结合使用时引出了另外一个问题.为了优化内存,当用户滚 动时这些组件回收了子视图.如果每个子视图触发一个AsyncTask,当它完成时没法保证,相关的视图还没有被回收时已经用在了别的子视图当中.此外, 还有异步任务开始的顺序是不能保证他们完成的顺序.

这篇文章透过Multithreading for Performance功能讨论处理并发,并且提供了一个当任务完成后ImageView将一个引用存储到后面能被检查的AsyncTask的解决方案. 使用类似的方法,从上一节的AsyncTask可以扩展到遵循类似的模式.

创建一个专用的Drawable的子类来存储一个引用备份到工作任务中.在这种情况下,一个BitmapDrawable被使用以便任务完成后一个占位符图像可以显示在ImageView中:

  1. static class AsyncDrawable extends BitmapDrawable { 
  2.     private final WeakReference bitmapWorkerTaskReference; 
  3.  
  4.     public AsyncDrawable(Resources res, Bitmap bitmap, 
  5.             BitmapWorkerTask bitmapWorkerTask) { 
  6.         super(res, bitmap); 
  7.         bitmapWorkerTaskReference = 
  8.             new WeakReference(bitmapWorkerTask); 
  9.     } 
  10.  
  11.     public BitmapWorkerTask getBitmapWorkerTask() { 
  12.         return bitmapWorkerTaskReference.get(); 
  13.     } 
  14.  } 

执行BitmapWorkerTask前,你创建一个AsyncDrawable,并将其绑定到目标ImageView:

  1. public void loadBitmap(int resId, ImageView imageView) { 
  2.     if (cancelPotentialWork(resId, imageView)) { 
  3.         final BitmapWorkerTask task = new BitmapWorkerTask(imageView); 
  4.         final AsyncDrawable asyncDrawable = 
  5.                 new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); 
  6.         imageView.setImageDrawable(asyncDrawable); 
  7.         task.execute(resId); 
  8.     } 
  9.  } 

如果别的正在运行的任务已经和这个ImageView关联,cancelPotentialWork引用在上面的代码示例检查中.如果这样,它试图通过调用cancel()取消先前的任务.在少数情况下,新的任务数据匹配现有的任务,而且并不需要做什么.下面是实现 cancelPotentialWork:

  1. public static boolean cancelPotentialWork(int data, ImageView imageView) { 
  2.     final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); 
  3.     if (bitmapWorkerTask != null) { 
  4.         final int bitmapData = bitmapWorkerTask.data; 
  5.         if (bitmapData != data) { 
  6.             // Cancel previous task 
  7.             bitmapWorkerTask.cancel(true); 
  8.         } else { 
  9.             // The same work is already in progress 
  10.             return false
  11.         } 
  12.     } 
  13.     // No task associated with the ImageView, or an existing task was cancelled 
  14.     return true
  15.  } 

一个帮助方法,getBitmapWorkerTask(),使用以上来检索一个和特定ImageView相关的任务:

  1. private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { 
  2.    if (imageView != null) { 
  3.        final Drawable drawable = imageView.getDrawable(); 
  4.        if (drawable instanceof AsyncDrawable) { 
  5.            final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; 
  6.            return asyncDrawable.getBitmapWorkerTask(); 
  7.        } 
  8.     } 
  9.     return null
  10.  } 

这***一步是在BitmapWorkerTask更新onPostExecute()方法,以便任务取消时并且当前任务和这个ImageView关联时进行检查:

  1. class BitmapWorkerTask extends AsyncTask { 
  2.     ... 
  3.     @Override 
  4.     protected void onPostExecute(Bitmap bitmap) { 
  5.         if (isCancelled()) { 
  6.             bitmap = null
  7.         } 
  8.         if (imageViewReference != null && bitmap != null ) { 
  9.             final ImageView imageView = imageViewReference.get(); 
  10.             final BitmapWorkerTask bitmapWorkerTask = 
  11.                     getBitmapWorkerTask(imageView); 
  12.             if (this == bitmapWorkerTask && imageView != null) { 
  13.                 imageView.setImageBitmap(bitmap); 
  14.             } 
  15.         } 
  16.     } 
  17.  } 

现在这个实现适合使用ListView和GridView控 件组件以及回收其子视图的任何其他组件.在你正常地给你的ImageView控件设置图片时简单地调用loadBitmap就行了.例如,在一个 GridView中实现的方式是在支持的适配中的[android.view.View, android.view.ViewGroup) getView()](http://docs.eoeandroid.com/reference/android/widget /Adapter.html#getView(int,)方法中.

责任编辑:闫佳明 来源: my.eoe.cn
相关推荐

2014-04-08 14:19:06

Android开发UI线程

2009-03-19 15:52:50

Silverlight位图WPF

2010-09-14 09:38:48

AndroidUI

2024-10-24 17:13:55

WinformUI多线程

2015-11-18 18:56:36

Java多线程处理

2017-03-13 10:41:33

iOSUI操作主线程

2009-04-10 22:28:29

企业安全信息安全RSA

2009-07-17 10:37:05

C#多线程

2021-01-11 16:29:08

加密货币金融银行

2009-11-27 08:50:51

Chrome OSWindows

2021-08-27 22:07:55

Oracle索引位图

2015-08-07 10:40:31

UI主线程

2009-07-15 18:06:38

Swing线程

2024-04-30 12:56:00

多线程.NET

2010-04-14 09:20:26

.NET多线程

2009-04-10 14:38:17

Oracle高手位图索引

2010-04-08 10:57:04

Oracle编程

2024-06-18 08:22:51

ActivityUI线程

2024-05-16 12:51:15

WinForms线程UI

2022-05-19 10:04:15

UIAndroid子线程
点赞
收藏

51CTO技术栈公众号