Processing Bitmaps Off the UI Thread不在UI线程中处理Bitmaps(Android官方翻译文档2)

来源:互联网 发布:工艺流程优化的案例 编辑:程序博客网 时间:2024/06/01 09:28

原文地址:http://developer.android.com/training/displaying-bitmaps/load-bitmap.html


注:我的英文水平确实有限,希望大家能够指出不足之处:——),建议多阅读官方文档,市场上很多书将思想讲的比较少,一来就是组件和熟悉。。。简直无语

我接下来写的关于Android的文档基本以翻译为主,原因有:1、个人能力有限,不想耽误童鞋们。。

我把我个人觉得相对重要的用红色标注了



ProcessingBitmaps Off the UI Thread

不在UI线程中处理Bitmaps

The BitmapFactory.decode* methods, discussed inthe Load Large BitmapsEfficiently lesson,should not be executed on the main UI thread if the source data is read fromdisk or a network location (or really any source other than memory). The timethis 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.). Ifone of these tasks blocks the UI thread, the system flags your application asnon-responsive and the user has the option of closing it (see Designing forResponsiveness for moreinformation).

This lesson walks you throughprocessing bitmaps in a background thread using AsyncTask and showsyou how to handle concurrency issues.

如果资源数据(resource data)是从磁盘或者是从网络中(或者是内存以外的任何其他来源)中读取,那么在Load LargeBitmaps Efficiently这个课程中讨论的BitmapFactory.decode* 方法不应该在主线程中执行。加载data所占用的时间是不可预见的并且取决于各种因素(从磁盘或网络中读取的速度,图片的大小,CPU的能力等等),如果这些任务中的一个阻塞了UI线程,那么系统就会标记你的application为无反应并且用户能够选择关闭它(看Designingfor Responsiveness 获取更多信息)

 

这节课将引导你在后台线程处理bitmaps,通过使用异步任务(AsyncTask),并且告诉你如何处理并发问题(concurrencyissues.

 

Use anAsyncTask

使用异步任务

The AsyncTask class provides aneasy way to execute some work in a background thread and publish the resultsback on the UI thread. To use it, create a subclass and override the providedmethods. Here’s an example of loading a large image into an ImageView using AsyncTask and decodeSampledBitmapFromResource():

 

 AsyncTask 类提供了一种简单的方式来在后台线程执行一些任务并且产生结果给UI线程。要使用它,就要创建一个子类并override它提供的方法,这里有一个使用AsycTaskdecodeSampledBitmapFromResource()加载大图到ImageView的一个例子

 

class BitmapWorkerTask extends AsyncTask<Integer,Void,Bitmap> {
    private finalWeakReference<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 imagein background.
    @Override
    protected Bitmap doInBackground(Integer...params){
        data = params[0];
        returndecodeSampledBitmapFromResource(getResources(), data,100,100));
    }

    // Oncecomplete, 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);
            }
        }
    }
}

The WeakReference to the ImageView ensures that the AsyncTask does not preventthe ImageView and anything itreferences from being garbage collected. There’s no guarantee the ImageView is still aroundwhen the task finishes, so you must also check the reference in onPostExecute(). The ImageView may no longerexist, if for example, the user navigates away from the activity or if aconfiguration change happens before the task finishes.

 

ImageViewWeakReference使得 AsyncTask不会阻止ImageView和任何它的引用被垃圾回收。这里不能够确保党任务完成的时候ImageView还在,所以你必须在onPostExecute中检查。ImageView可能不存在了,比如说在任务finish之前,用户离开了Activity或者是配置发生了改变(典型的是屏幕发生了旋转)

 

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

要开始异步加载bitmap,只需要创建一个新task并且execute它

 

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

 

HandleConcurrency

处理并发

Common view components such as ListView and GridView introduce anotherissue when used in conjunction with the AsyncTask as demonstrated inthe previous section. In order to be efficient with memory, these componentsrecycle child views as the user scrolls. If each child view triggers an AsyncTask, there isno guarantee that when it completes, the associated view has not already beenrecycled for use in another child view. Furthermore, there is no guarantee thatthe order in which asynchronous tasks are started is the order that theycomplete.

The blog post Multithreading forPerformance furtherdiscusses dealing with concurrency, and offers a solution where the ImageView stores a reference tothe most recent AsyncTask which can later bechecked when the task completes. Using a similar method, the AsyncTask from the previoussection can be extended to follow a similar pattern.

通常一些组件,比如ListView、GridView,当连续使用上一节所讲的异步任务时会引入另一个问题。为了更高效使用memory,当用户滚动的时候这些组件循环使用child views,

如果每个child view 触发一个AsyncTask,那么这将不能够保证当这个任务完成的时候,在另一个child view中该任务所关联的视图还没有被回收使用(这句我翻译的有点问题,根据下文的意思,应该是错位吧。。。如果一个人使劲滑动list,一个ImageView对象可能会被多次使用。),此外,也不能够确保that每个异步任务完成的顺序和开始的顺序是否是相同的。

 

博客发表了 Multithreadingfor Performance长远讨论了处理并发,并且提供了一个解决方案,在ImageView中存储了对最新的AsyncTask的引用,当任务完成时该AsyncTask可能被检测到。(利用instanceof关键字)。使用一个相似的方法,前一节中的AsyncTask能够被扩展成相似的模式

 

Create a dedicated Drawable subclass to store areference back to the worker task. In this case, a BitmapDrawableis used sothat a placeholder image can be displayed in the ImageView while the taskcompletes:

创建一个Drawable的一个特定的子类来存储worker task的引用。在这个情况下,使用BitmapDrawable,因此当task完成的时候, a placeholder image(该图片被task标记了)能够被展示在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();    }}

Before executing the BitmapWorkerTask, you create an AsyncDrawable and bind it to the target ImageView:

 

在执行BitmapWorkerTask时,你需要创建一个AsyncDrawable 并且把它和目标Imageview进行绑定

public void loadBitmap(int resId, ImageView imageView) {    if (cancelPotentialWork(resId, imageView)) {        final BitmapWorkerTask task = new BitmapWorkerTask(imageView);        finalAsyncDrawable asyncDrawable =                newAsyncDrawable(getResources(), mPlaceHolderBitmap, task);(把bitmaptask通过绑定在一起)        imageView.setImageDrawable(asyncDrawable);        task.execute(resId);    }}

 

The cancelPotentialWork method referencedin the code sample above checks if another running task is already associatedwith the ImageView. If so, itattempts to cancel the previous task by calling cancel(). In a small numberof cases, the new task data matches the existing task and nothing further needsto happen. Here is the implementation of cancelPotentialWork:

 

上面代码中引用的cancelPotentialWork 方法用来检测是否另外的一个runningtask已经关联了ImageView。如果是这样的话,就要通过调用cancel()取消前一个task。很少情况下,新任务的数据会匹配存在的任务,并且接下来不再需要发生任何事。这里是 cancelPotentialWork:的实现:

 

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

A helper method, getBitmapWorkerTask(), is used above toretrieve the task associated with a particularImageView:

上面用到的另外一个方法, getBitmapWorkerTask()是用来根据指定的ImageView来检测其所关联的任务(使用 instanceof关键字)

 

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

 

The last step is updating onPostExecute() in BitmapWorkerTask so that it checks if the task is cancelled and if thecurrent task matches the one associated with the ImageView:

 

最后一步是跟新onPostExecute() in BitmapWorkerTask。它检查是否任务已经取消了(调用isCancelled()方法),还有是否当前任务匹配关联了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);            }        }    }}

This implementation is nowsuitable for use in ListView and GridView components as wellas any other components that recycle their child views. Simply call loadBitmap where you normallyset an image to yourImageView. For example, in a GridView implementation thiswould be in the getView() method of thebacking adapter.

 

这种实现现在适合于使用lvgv组件还有其他会循环使用子视图的组件。当需要把image放到ImageView中时,只需简单地调用loadBitmap。比如在实现GV的时候,在adapter中的getView()方法中调用就好了。

 

(完)


转载请注明出处:http://blog.csdn.net/storyofliu_yu

0 0
原创粉丝点击