Android高效显示图片详解(二)

    

       实际的使用环境中,若是图片来源是SD卡或者网络,那那么加载图片的过程必定不要放在UI线程中,这样会严重的阻塞UI线程,出现ANR,程序就废了。所以咱们首先要实现异步加载。

第一步:利用AsyncTask实现图片的异步加载

java

将decodeSampledBitmapFromResource方法放入Task的doInBackground中后台执行。不熟悉AsyncTask的同窗能够学习AsyncTask的相关知识,这里再也不过多介绍。缓存

代码:网络

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

注意,这里对ImageView使用 WeakReference弱引用的目的是确保 AsyncTask不会妨碍系统对ImageView必要时候的垃圾回收。不然可能会出现内存泄露,同时,咱们必定要在Task执行完毕后对ImageView的存在性进行判断,由于不能保证Task执行完毕后,ImageView还会存在。并发

下来咱们按照下面的代码就可使用这个Task了:异步

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

第二步:处理并发状况async


ListView与GridView这种多子视图的控件会出现两个问题,

ide

第一,一个ListView会有众多的ChildView,为了更高效的利用内存,控件会自动回收掉被用户滑动过去,不在当前有显示的ChildView,若是每个ChildView都开启一个Task去加载图片,这样就不能保证开启Task的ChildView在Task执行完毕后没有被回收掉(颇有可能用户滑动到其余地方去了)。


第二,由于每张图片的处理时间是不一样的,所以一样不能保证加载完成的次序与开始的次序一致。


下来咱们开始着手解决这些问题,咱们要让ImageView与Task造成一种绑定的关系。

咱们先来建立一个特殊的Drawable,这个Drawable有两个功能,一个是与Task造成一种绑定的关系,另外也充当了ImageView的临时占位图像,该Drawable的代码以下:函数

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

在该Drawable中经过弱引用能与对应的Task造成一种一一对应的捆绑关系。学习

咱们能够这样使用它,在执行Task以前,先建立一个对应的Drawable,并把它当成将要呈现实际图片的ImageView占位图片,同时也与ImageView造成了绑定关系。this

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

固然,咱们须要判断下ImageView以前是否已经绑定了,若是以前绑定过但与本次的图片不一样,那咱们就要按最新的须要重新绑定下,若是以前与如今的一致,则保持原状,再也不重新绑定,代码中的cancelPotentialWork就是作这个工做的,其代码以下:

public static boolean cancelPotentialWork(int data, ImageView imageView) {
    final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
    if (bitmapWorkerTask != null) {
        final int bitmapData = bitmapWorkerTask.data;
        if (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;
}

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

最后,咱们在Task的onPostExecute函数中,把加载的图片更新到视图中去,在更新前咱们须要检查下Task是否被取消,而且当前的Task是不是那个与ImageView关联的Task,一致则咱们把图片更新到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);
            }
        }
    }
}

最后,实际的使用也至关简单,只须要在你的ListView适配器的getView函数中调用上面的loadBitmap函数就OK了~

下一节咱们来讲说缓存,加入缓存让这个机制更增强大。。

感谢收看! 多多好评,在此谢过!

相关文章
相关标签/搜索