3 Processing Bitmaps Off the UI Thread(处理UI线程的位图)

The BitmapFactory.decode* methods, discussed in the Load Large Bitmaps Efficiently lesson, should not be executed on the main UI thread if the source data is read from disk or a network location (or really any source other than memory). 

在"高效加载大位图"这节课我们讨论了BitmapFactory.decode*方法,如果数据来源是读取网络或者磁盘不要在主UI线程处理(或则确认是除了内存的其他源)。

The time this data takes to load is unpredictable and depends on a variety of factors (speed of reading from disk or network, size of image, power of CPU, etc.). 

加载数据的时间是无法预测的,而且取决于各种因素(网络和磁盘的读取速度,图像尺寸,CPU功率,等等)。

If one of these tasks blocks the UI thread, the system flags your application as non-responsive and the user has the option of closing it (see Designing for Responsiveness for more information).

如果这些事物中的一个阻塞UI线程,系统会停止你的应用提示无响应和用户会有一个关闭它的选项()

This lesson walks you through processing bitmaps in a background thread using AsyncTask and shows you how to handle concurrency issues.

这节课让你了解处理位图在一个后台线程使用AsyncTask和展示怎样处理并发问题。

Use an AsyncTask(使用异步任务)


The AsyncTask class provides an easy way to execute some work in a background thread and publish the results back on the UI thread.
AsyncTask类提供容易的方法处理一些工作在后台线程然后发表结果返回UI线程。

To use it, create a subclass and override the provided methods. Here’s an example of loading a large image into an ImageView using AsyncTask and decodeSampledBitmapFromResource():
这样使用他,创建一个子类并且重写提供的方法,这有一个例子,加载大的图像到ImageView使用AsyncTask和decodeAampledBitmapFramResource():

[java]  view plain copy
  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     private final WeakReference<ImageView> 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.     // 使用一个弱引用来确保ImageView无用的时候被回收  
  8.         imageViewReference = new WeakReference<ImageView>(imageView);  
  9.     }  
  10.   
  11.     // Decode image in background.(解码图像在后台)  
  12.     @Override  
  13.     protected Bitmap doInBackground(Integer... params) {  
  14.         data = params[0];  
  15.         return decodeSampledBitmapFromResource(getResources(), data, 100100));  
  16.     }  
  17.   
  18.     // Once complete, see if ImageView is still around and set bitmap.  
  19.     // 一旦完成,查看ImageView如果还在设置位图  
  20.     @Override  
  21.     protected void onPostExecute(Bitmap bitmap) {  
  22.         if (imageViewReference != null && bitmap != null) {  
  23.             final ImageView imageView = imageViewReference.get();  
  24.             if (imageView != null) {  
  25.                 imageView.setImageBitmap(bitmap);  
  26.             }  
  27.         }  
  28.     }  
  29. }  


The WeakReference to the ImageView ensures that the AsyncTask does not prevent the ImageView and anything it references from being garbage collected.

ImageView使用弱引用,确保AsyncTask不能阻止ImageView和任何他的引用被垃圾回收。

There’s no guarantee the ImageView is still around when the task finishes, so you must also check the reference in onPostExecute().

这不能确保当任务完成时ImageView还在,所以你还必须检查引用在onPostExeute()方法里。

The ImageView may no longer exist, if for example, the user navigates away from the activity or if a configuration change happens before the task finishes.

ImageView可能不会长时间存在,例如,如果用户退出activity或者布局发生了改变在任务结束前。

To start loading the bitmap asynchronously, simply create a new task and execute it:

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

[java]  view plain copy
  1. public void loadBitmap(int resId, ImageView imageView) {  
  2.     BitmapWorkerTask task = new BitmapWorkerTask(imageView);  
  3.     task.execute(resId);  
  4. }  

Handle Concurrency(操作并发)


Common view components such as ListView and GridView introduce another issue when used in conjunction with the AsyncTask as demonstrated in the previous section.

普通的视图控件例如listview和gridview会有其他的问题,当联合AsyncTask使用的时候在之前的章节演示过。

In order to be efficient with memory, these components recycle child views as the user scrolls.

为了以后高效的使用内存,这些组件回收子视图当用户滚动时。

If each child view triggers an AsyncTask, there is no guarantee that when it completes, the associated view has not already been recycled for use in another child view.

如果每个子视图启动一个AsyncTask,这不能完全保证,关联的视图没有准备回收给其他视图中使用。

Furthermore, there is no guarantee that the order in which asynchronous tasks are started is the order that they complete.

此外,不能保证异步任务的完成顺序和开始顺序一样。

The blog post Multithreading for Performance further discusses dealing with concurrency, and offers a solution where the ImageView stores a reference to the most recent AsyncTask which can later be checked when the task completes.

博客发布《多线程性能》详细讨论了并发处理,并且提供一个解决方法,ImageView储存一个标记在最近的AsyncTask,过后当任务完成时被检查。

Using a similar method, the AsyncTask from the previous section can be extended to follow a similar pattern.

使用类似的方法,前面部分的AsyncTask可以扩展成同样的模型。

Create a dedicated Drawable subclass to store a reference back to the worker task.

创建一个专门储存标记的Drawable子类回调工作任务。

In this case, a BitmapDrawable is used so that a placeholder image can be displayed in the ImageView while the task completes:

在这个方案中,当任务完成时一个使用BitmapDrawable的位置标记的图像被显示在ImageView.

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

Before executing the BitmapWorkerTask, you create an AsyncDrawable and bind it to the target ImageView:
在执行BitmapWorkerTask之前,你要创建一个AsyncDrawable并且绑定到目标ImageView.

[java]  view plain copy
  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. }  

The cancelPotentialWork method referenced in the code sample above checks if another running task is already associated with the ImageView.
ancelPotentialWork方法引用上面示例代码检查ImageView是否已经其他正在运行的事务关联。
If so, it attempts to cancel the previous task by calling cancel().
如果是,他调用cancel()方法试图取消先前的事务。
In a small number of cases, the new task data matches the existing task and nothing further needs to happen.
在一部分case里,新事务数据的符合正在执行的事务而且不会发生任何事在接下来。
Here is the implementation of cancelPotentialWork:
这是cancelPotentialWork的实现:
[java]  view plain copy
  1. public static boolean cancelPotentialWork(int data, ImageView imageView) {  
  2.     final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);  
  3.   
  4.     if (bitmapWorkerTask != null) {  
  5.         final int bitmapData = bitmapWorkerTask.data;  
  6.         if (bitmapData != data) {  
  7.             // Cancel previous task(取消之前的事务)  
  8.             bitmapWorkerTask.cancel(true);  
  9.         } else {  
  10.             // The same work is already in progress  
  11.             return false;  
  12.         }  
  13.     }  
  14.     // No task associated with the ImageView, or an existing task was cancelled  
  15.     return true;  
  16. }  

A helper method, getBitmapWorkerTask(), is used above to retrieve the task associated with a particular ImageView:
一个帮助方法被上面示例取回事务关联的特定imageView:

[java]  view plain copy
  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. }  

The last step is updating onPostExecute() in BitmapWorkerTask so that it checks if the task is cancelled and if the current task matches the one associated with the ImageView:
最后一步在BitmapWorkerTask里onPostExecute()检查,事务是被取消还是最近匹配相关联的ImageView.

[java]  view plain copy
  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {  
  2.     ...  
  3.   
  4.     @Override  
  5.     protected void onPostExecute(Bitmap bitmap) {  
  6.         if (isCancelled()) {  
  7.             bitmap = null;  
  8.         }  
  9.   
  10.         if (imageViewReference != null && bitmap != null) {  
  11.             final ImageView imageView = imageViewReference.get();  
  12.             final BitmapWorkerTask bitmapWorkerTask =  
  13.                     getBitmapWorkerTask(imageView);  
  14.             if (this == bitmapWorkerTask && imageView != null) {  
  15.                 imageView.setImageBitmap(bitmap);  
  16.             }  
  17.         }  
  18.     }  
  19. }  

This implementation is now suitable for use in ListView and GridView components as well as any other components that recycle their child views.
这个工具目前适合在ListView和GridView控件中使用比起其他循环子视图的控件。
Simply call loadBitmap where you normally set an image to your ImageView.
仅仅调用loadBitmap通常设置在你的ImageView里
For example, in a GridView implementation this would be in the getView() method of the backing adapter.
例如,在GridView后台adapter中的getView()方法使用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值