高效加载图片(三)

原创
2014/11/26 14:34
阅读数 13

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

在非UI线程中处理图片

        BitmapFactory.decode*方法,已在Load Large BItmaps Efficiently 章节中讨论了,如果资源数据从磁盘或是网络获取的话不应该在UI线程中执行。图片数据的获取所用的时间是未知的,依赖于很多条件。如果其中的一个堵塞了主线程,会导致应用无响应。

        这节课通过展示使用AsyncTask在后台线程中处理图片加载的流程,来解决死锁的问题。

使用AsyncTask

    AsyncTask类提供了一个简单的方法让一些任务在后台线程中执行,并把结果返回到UI线程中。下面是一个使用AstncTask和decodeSampleBitmapFromResource()加载一个大图到ImageView的实现的例子:

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

使用WeakReference存储ImageView,可以使AsyncTask不会阻止ImageView被垃圾回收。因为无法保证在任务结束后ImageView还存在,所以在onPostExecute()方法中也必须要校验。

并发处理

        如果在ListView和GridView这样的组件中像上一节那样使用AsyncTask会导致另一种结果。为了有效的利用缓存,这些组件在滚动的过程中会复用子控件。如果每个子控件只出发一个任务,那个无法避免当这个任务完成的时候,这个任务关联的控件正用于展示其他子视图。

        在博客MultithreadingforPerformance中讨论并发行为,并给出了一个解决方案,通过ImageView存储一个最近的任务AsyncTask,这样当任务完成时能够被处理。

        创建一个专门的Drawable子类存储回调的任务。这样当任务返回时就可以利用这个图片来展示在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();
    }
}

在执行BitmapWorkerTask任务前,需要创建一个AsyncDrawable并绑定到目标ImagView

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

cancelPotentialWork是取消绑定目标ImageView的上一个任务

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

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

在任务的onPostExecute()方法需要判断任务的cancelled状态

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


展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部