Android异步加载图片(一)下载完图片之后,如何更新界面
场景
想象一下加载图片的时候,ImageView首先设置一个placeHolder,然后开启AsyncTask去加载合适的图片,图片加载成功之后, 怎么去更新ImageView?一般来讲,有两种处理方式,第一种:将ImageView传递给AsyncTask,task执行完之后,直接ImageView设置bitmap,第二种:Task执行完毕之后,将Bitmap存储在缓存里,通知主线程的view,进行数据更新。
将ImageView传递给AsyncTask
要求
- ListView或者GridView会复用ImageView,例如pos为4的ImageView4开启了一个AsyncTask,用户滚动到了pos为8的位置,复用了ImageView4,即使这个时候AsyncTask更新完了,但是也不能去更新ImageView4。
- AsyncTask不能阻止ImageView的内存释放,例如当用户离开这个页面的时候,应该销毁界面,AsyncTask应当避免造成内存泄露
- AsyncTask执行完毕之后,应该能够拿到ImageView,并且ImageView设置Bitmap之前需要判断ImageView与Drawable和Task之间必须一一对应
实现
- BitmapWorkerTask继承AsyncTask,弱引用ImageView,这样AsyncTask既可以在执行完毕之后取到ImageView更新Bitmap,又不会阻止ImageView被释放
- ImageView每次都会设置一个placeHolder AsyncDrawable,在AsyncDrawable中弱引用AsyncTask,当AsyncTask执行完毕,可以通过ImageView get AsyncDrawable,然后通过AsyncDrawable获取到BitmapTask,判断task是否是同一个task,如果是,那么就能确定ImageView,AsyncDrawable,AsyncTask一一对应,可以进行数据更新,此外AsyncTask完全执行完毕之后,也可以直接被释放掉了,AsyncDrawable不会阻碍AsyncTask被释放
Task执行完毕之后,将Bitmap存储在缓存里,通知主线程的view,进行数据更新
缺点:
- 盲目刷新,效率太低,特别是ImageView特别多,AsyncTask执行很快,那么刷新的频率会特别高,这样会导致页面不断刷新,甚至卡顿
总结
ImageView,BitmapWorkerTask,AsyncDrawable的关系
- BitmapWorkerTask持有一个WeakReference
imageViewReference,弱引用ImageView,用作异步处理加载图片的任务。 - AsyncDrawable巧妙的引用持有弱引用WeakReference
bitmapWorkerTaskReference,AsyncDrawable继承自BitmapDrawable,这样ImageView就可以setImageBitmap(AsyncDrawable) - AsyncDrawable中弱引用BitmapWorkerTask,ImageView.getDrawable又可以获得AsyncDrawable,继而获得BitmapWorkerTask
关键代码
protected 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();
}
}
protected class BitmapWorkerTask extends AsyncTask<Object, Void, BitmapDrawable> {
private Object data;
private final WeakReference<ImageView> imageViewReference;
public BitmapWorkerTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
final AsyncDrawable asyncDrawable =
new AsyncDrawable(mResources, map, task);
imageView.setImageDrawable(asyncDrawable);
protected void onPostExecute(BitmapDrawable value) {
final ImageView imageView = getAttachedImageView();
if (value != null && imageView != null) {
imageView.setImageDrawable(value);
afterGetBitmap(imageView, value);
}
}