实际的使用环境中,若是图片来源是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了~
下一节咱们来讲说缓存,加入缓存让这个机制更增强大。。
感谢收看! 多多好评,在此谢过!