This lesson teaches you to
You should also read
Try it out
DisplayingBitmaps.zip
       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). 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.). 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).
      
       This lesson walks you through processing bitmaps in a background thread using
       
        
         AsyncTask
        
       
       and shows you how to handle concurrency issues.
      
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. 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()
        
       
       :
      
       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);
            }
        }
    }
}
      
      
       The
       
        
         WeakReference
        
       
       to the
       
        
         ImageView
        
       
       ensures that the
       
        
         AsyncTask
        
       
       does not prevent the
       
        
         ImageView
        
       
       and anything it
references from being garbage collected. There’s no guarantee the
       
        
         ImageView
        
       
       is still around when the task finishes, so you must also check the reference in
       
        
         onPostExecute()
        
       
       . 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.
      
To start loading the bitmap asynchronously, simply create a new task and execute it:
       public void loadBitmap(int resId, ImageView imageView) {
    BitmapWorkerTask task = new BitmapWorkerTask(imageView);
    task.execute(resId);
}
      
      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. 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. 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. Using a similar method, the
       
        
         AsyncTask
        
       
       from the previous section can be extended to follow a similar pattern.
      
       Create a dedicated
       
        
         Drawable
        
       
       subclass to store a reference
back to the worker task. In this case, a
       
        
         BitmapDrawable
        
       
       is used so
that a placeholder image can be displayed in the
       
        
         ImageView
        
       
       while the task
completes:
      
       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
        
       
       :
      
       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);
    }
}
      
      
       The
       
        cancelPotentialWork
       
       method referenced in the code sample above checks if another
running task is already associated with the
       
        
         ImageView
        
       
       . If so, it attempts to
cancel the previous task by calling
       
        
         cancel()
        
       
       . In a small number
of cases, the new task data matches the existing task and nothing further needs to happen. Here is
the implementation of
       
        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 to retrieve the task associated
with a particular
       
        
         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;
}
      
      
       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
        
       
       :
      
       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 now suitable for use in
       
        
         ListView
        
       
       and
       
        
         GridView
        
       
       components as well as any other components that recycle their child
views. Simply call
       
        loadBitmap
       
       where you normally set an image to your
       
        
         ImageView
        
       
       . For example, in a
       
        
         GridView
        
       
       implementation this
would be in the
       
        
         getView()
        
       
       method of the backing adapter.